/*
 * Copyright (c) 2019 Clementine Computing LLC.
 *
 * This file is part of PopuFare.
 * 
 * PopuFare is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * PopuFare is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 * 
 * You should have received a copy of the GNU Affero General Public License
 * along with PopuFare.  If not, see <https://www.gnu.org/licenses/>.
 *
 */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "../common/common_defs.h"

typedef struct rfid_decode_pattern_struct
{
    unsigned int bits;

    unsigned int shift;
    unsigned long long mask;
    unsigned long long match;

    unsigned int site_shift;
    unsigned long long site_mask;
    
    unsigned int id_shift;
    unsigned long long id_mask;
    
} rfid_decode_pattern;

static rfid_decode_pattern rfid_pattern_buffer[NUM_RFID_PATTERNS] = {{0}};
static int rfid_pattern_used = -1;

int rfid_pattern_loaded()
{
    return rfid_pattern_used;
}

int load_rfid_decode_patterns(char *filename)
{
    char line[LINE_BUFFER_SIZE];
    int i, retval;
    FILE *f;
    char *c;
    
    if(!filename)
    {
        fprintf(stderr, "Cannot open RFID Pattern Match file %s", "(null)");
        rfid_pattern_used = -1;
        return -1;
    }
    
    i = 0;
    f = fopen(filename, "rb");
    
    if(!f)
    {
        fprintf(stderr, "Cannot open RFID Pattern Match file %s", filename);
        rfid_pattern_used = -1;
        return -1;
    }
    
    while(i < NUM_RFID_PATTERNS)
    {
        fgets(line, LINE_BUFFER_SIZE, f);

        if(feof(f))
        {
            break;
        }
        
        c = line;
        
        //skip any blank lines, or comment lines starting with a # as their first non-whitespace character
        while( (*c == ' ') || (*c == '\t') || (*c == '\r') || (*c == '\n') ) c++;
        if( (*c == '#') || (*c == '\0') ) continue;
        
        retval = sscanf(c, "%x %x %llx %llx %x %llx %x %llx", &rfid_pattern_buffer[i].bits, &rfid_pattern_buffer[i].shift, 
                                                             &rfid_pattern_buffer[i].mask, &rfid_pattern_buffer[i].match, 
                                                             &rfid_pattern_buffer[i].site_shift, &rfid_pattern_buffer[i].site_mask, 
                                                             &rfid_pattern_buffer[i].id_shift, &rfid_pattern_buffer[i].id_mask
                                                            );
        
        if(retval == 8)
        {
            i++;
        }
        else
        {
            memset(&rfid_pattern_buffer[i], 0, sizeof(rfid_decode_pattern));
            fprintf(stderr, "Cannot parse RFID Pattern Match line \"%s\"\n", line);
        }
    }

    fclose(f);

    if(i >= NUM_RFID_PATTERNS)
    {
        fprintf(stderr, "RFID Pattern Match file %s contains more than %d records!", filename, NUM_RFID_PATTERNS);
        rfid_pattern_used = -1;
        return -1;
    }
    
     memset(&rfid_pattern_buffer[i], 0, sizeof(rfid_decode_pattern));
    
     rfid_pattern_used = i;
     return i;   
}


int decode_rfid_string(char *raw_rfid, unsigned long long *site, unsigned long long *id, unsigned int *bits)
{
    unsigned long long raw;
    unsigned int nbits;
    char *rest;
    
    int i;
    
    if(!raw_rfid)
    {
        fprintf(stderr, "NULL RFID String!\n");
        return -1;
    }

    if(rfid_pattern_used < 1)
    {
        fprintf(stderr, "rfid_pattern_used < 1!\n");
    }

    nbits = strtol(raw_rfid, &rest, 16);
    
    if(*rest++ != '|')
    {
        fprintf(stderr, "Malformed RFID String!\n");
        return -1;
    }

    raw = strtoll(rest, NULL, 16);

    for(i = 0; i < rfid_pattern_used; i++)
    {
        //If this RFID string has the right number of bits AND matches our shift-and-mask criteria
        if( (nbits == rfid_pattern_buffer[i].bits) && 	
            (((raw >> rfid_pattern_buffer[i].shift) & rfid_pattern_buffer[i].mask) == rfid_pattern_buffer[i].match) 
          )
        {
            if(bits)
            {
                *bits = nbits;
            }
            
            if(site)
            {
                *site = (raw >> rfid_pattern_buffer[i].site_shift) & rfid_pattern_buffer[i].site_mask;
            }

            if(id)
            {
                *id = (raw >> rfid_pattern_buffer[i].id_shift) & rfid_pattern_buffer[i].id_mask;
            }
            
            return 0;
        }
    }

    return -1;
}


