#include <stdlib.h>
#include <panda/date/parse.h>
using namespace panda::time;

namespace panda { namespace date {

const int _P_STATE_YEAR  = 0;
const int _P_STATE_MONTH = 1;
const int _P_STATE_DAY   = 2;
const int _P_STATE_HOUR  = 3;
const int _P_STATE_MIN   = 4;
const int _P_STATE_SEC   = 5;

err_t parse_iso (const char* str, size_t len, datetime* date) {
    if (len < 1) len = strlen(str);
    int state = _P_STATE_YEAR;
    int32_t curval = 0;
    const char* strend = str + len;
    for (; str <= strend; str++) {
        char c = (str == strend) ? '-' : *str;
        if (c >= '0' and c <= '9') {
            curval *= 10;
            curval += (c-'0');
        }
        else if (c == '-' || c == ' ' || c == ':' || c == '/' || c == '.' || c == '\n' || c == 0) {
            switch (state) {
                case _P_STATE_YEAR:
                    date->year = curval;
                    break;
                case _P_STATE_MONTH:
                    date->mon = (curval == 0 ? 1 : curval) - 1;
                    break;
                case _P_STATE_DAY:
                    date->mday = curval == 0 ? 1 : curval;
                    break;
                case _P_STATE_HOUR:
                    date->hour = curval;
                    break;
                case _P_STATE_MIN:
                    date->min = curval;
                    break;
                case _P_STATE_SEC:
                    date->sec = curval;
                    break;
            }
            state++;
            curval = 0;
        }
        else return E_UNPARSABLE;
    }

    switch (state) { // fill absent fields with defaults
        case _P_STATE_MONTH:
            date->mon = 0;
        case _P_STATE_DAY:
            date->mday = 1;
        case _P_STATE_HOUR:
            date->hour = 0;
        case _P_STATE_MIN:
            date->min = 0;
        case _P_STATE_SEC:
            date->sec = 0;
    }
    
    return E_OK;
}

err_t parse_relative (const char* str, size_t len, datetime* date) {
    if (len < 1) len = strlen(str);
    memset(date, 0, sizeof(datetime)); // reset all values
    ptime_t curval = 0;
    bool negative = false;
    const char* strend = str + len;
    for (; str < strend; str++) {
        char c = *str;
        if (c == '-') negative = true;
        else if (c >= '0' and c <= '9') {
            curval *= 10;
            curval += (c-48);
        }
        else {
            if (negative) {
                curval = -curval;
                negative = false;
            }
            
            switch (c) {
                case 'Y':
                case 'y':
                    date->year = curval; break;
                case 'M':
                    date->mon = curval; break;
                case 'D':
                case 'd':
                    date->mday = curval; break;
                case 'h':
                    date->hour = curval; break;
                case 'm':
                    date->min = curval; break;
                case 's':
                    date->sec = curval; break;
            }
            
            curval = 0;
        }
    }

    return E_OK;
}

const unsigned char relchars[256] = {
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0,
    0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};

bool looks_like_relative (const char* str) {
    unsigned char c;
    const char* p = str;
    for (; (c = *p) != '\0'; p++) if (relchars[c]) return true;
    return p == str;
}

}}
