#pragma once
#ifndef MAIN_HEADER_H
#define MAIN_HEADER_H

#include <Arduino.h>
#include <WhillOmni.h>
#include <BorderXBeePacketizer.h>
#include <StopWatch.h>
#include <XBeeATCmds.h>
#include <MPU9250.h>
#include <PCA9624.h>
#include <RzmEepromMap.h>

using namespace Whill;

WhillOmni whill;
BorderXBeePacker packer;
BorderXBeeUnpacker unpacker;
XBeeATCmds xbeecmd;
MPU9250 mpu;
PCA9624 irled(0x60);
RzmEepromMap eepmap;
StopWatch stopwatch;

void printSpeedProfile(WhillOmni::LR lr)
{
    Serial.println("SPEED_PROFILE");
    Serial.print("dataset : "); Serial.println(whill.dataset(lr));
    Serial.print("mode    : "); Serial.println(whill.speedMode(lr));
    Serial.print("fwd spd : "); Serial.println(whill.forwardSpeed(lr));
    Serial.print("fwd acc : "); Serial.println(whill.forwardAccel(lr));
    Serial.print("fwd dec : "); Serial.println(whill.forwardDecel(lr));
    Serial.print("bwd spd : "); Serial.println(whill.reverseSpeed(lr));
    Serial.print("bwd acc : "); Serial.println(whill.reverseAccel(lr));
    Serial.print("bwd dec : "); Serial.println(whill.reverseDecel(lr));
    Serial.print("trn spd : "); Serial.println(whill.turnSpeed(lr));
    Serial.print("trn acc : "); Serial.println(whill.turnAccel(lr));
    Serial.print("trn dec : "); Serial.println(whill.turnDecel(lr));
}

void printSensorInfo(WhillOmni::LR lr)
{
    Serial.println("SENSOR_INFO");
    Serial.print("sensor acc x : "); Serial.println(whill.sensorAccelX(lr));
    Serial.print("sensor acc y : "); Serial.println(whill.sensorAccelY(lr));
    Serial.print("sensor acc z : "); Serial.println(whill.sensorAccelZ(lr));
    Serial.print("sensor gyr x : "); Serial.println(whill.sensorGyroX(lr));
    Serial.print("sensor gyr y : "); Serial.println(whill.sensorGyroY(lr));
    Serial.print("sensor gyr z : "); Serial.println(whill.sensorGyroZ(lr));
    Serial.print("joystick fb  : "); Serial.println(whill.joystickFront(lr));
    Serial.print("joystick lr  : "); Serial.println(whill.joystickSide(lr));
    Serial.print("battery      : "); Serial.println(whill.battery(lr));
    Serial.print("battery crnt : "); Serial.println(whill.batteryCurrent(lr));
    Serial.print("angle rad r  : "); Serial.println(whill.angleRadR(lr));
    Serial.print("angle rad l  : "); Serial.println(whill.angleRadL(lr));
    Serial.print("speed r      : "); Serial.println(whill.speedR(lr));
    Serial.print("speed l      : "); Serial.println(whill.speedL(lr));
    Serial.print("power state  : "); Serial.println(whill.powerState(lr));
    Serial.print("speed mode   : "); Serial.println(whill.speedModeIndicator(lr));
    Serial.print("error code   : "); Serial.println(whill.errorCode(lr));
}

void identify()
{
    pinMode(24, INPUT_PULLUP);
    pinMode(25, INPUT_PULLUP);
    pinMode(26, INPUT_PULLUP);
    pinMode(27, INPUT_PULLUP);

    Serial.print("bit 0 : "); Serial.println(digitalRead(27));
    Serial.print("bit 1 : "); Serial.println(digitalRead(26));
    Serial.print("bit 2 : "); Serial.println(digitalRead(25));
    Serial.print("bit 3 : "); Serial.println(digitalRead(24));

    HARDWARE_ID = (!digitalRead(24) << 3) | (!digitalRead(25) << 2) | (!digitalRead(26) << 1) | (!digitalRead(27) << 0);
    Serial.print("ID = "); Serial.println(HARDWARE_ID);
}

bool initEeprom(uint8_t hw_id)
{
    if (eepmap.checkHardwareID(hw_id))
    {
        Serial.println("EEPROM HW ID Check OK");
        return true;
    }
    else
    {
        Serial.print("EEPROM HW ID is different!! Writing Current ID = ");
        Serial.println(hw_id);
        eepmap.hardwareID(hw_id);
        b_need_xbee_init = true;
    }

    return false;
}

void setupXBee(uint8_t hw_id)
{
    xb_settings[0].channel = 0x12;
    xb_settings[0].pan_id = 0x07;
    xb_settings[0].dst_addr_h = 0x00000000;
    xb_settings[0].dst_addr_l = 0x0000FFFF;
    xb_settings[0].packetization_timeout = 10;

    xb_settings[1].channel = 0x13;
    xb_settings[1].pan_id = 0x08;
    xb_settings[1].dst_addr_h = 0x00000000;
    xb_settings[1].dst_addr_l = 0x0000FFFF;
    xb_settings[1].packetization_timeout = 10;

    xb_settings[2].channel = 0x14;
    xb_settings[2].pan_id = 0x09;
    xb_settings[2].dst_addr_h = 0x00000000;
    xb_settings[2].dst_addr_l = 0x0000FFFF;
    xb_settings[2].packetization_timeout = 10;

    xb_settings[3].channel = 0x15;
    xb_settings[3].pan_id = 0x0A;
    xb_settings[3].dst_addr_h = 0x00000000;
    xb_settings[3].dst_addr_l = 0x0000FFFF;
    xb_settings[3].packetization_timeout = 10;

    xb_settings[4].channel = 0x16;
    xb_settings[4].pan_id = 0x0B;
    xb_settings[4].dst_addr_h = 0x00000000;
    xb_settings[4].dst_addr_l = 0x0000FFFF;
    xb_settings[4].packetization_timeout = 10;

    xb_settings[5].channel = 0x17;
    xb_settings[5].pan_id = 0x0C;
    xb_settings[5].dst_addr_h = 0x00000000;
    xb_settings[5].dst_addr_l = 0x0000FFFF;
    xb_settings[5].packetization_timeout = 10;

    uint8_t index = hw_id;
    if (hw_id > 5) index = 0;

    xbeecmd.channel(xb_settings[index].channel);
    xbeecmd.panID(xb_settings[index].pan_id);
    xbeecmd.dstAddrH(xb_settings[index].dst_addr_h);
    xbeecmd.dstAddrL(xb_settings[index].dst_addr_l);
    xbeecmd.baudrate(0x5);
    xbeecmd.packtizationTimeout(xb_settings[index].packetization_timeout);

    xbeecmd.save();
    xbeecmd.apply();
    xbeecmd.reset();
    // xbeecmd.exit();
    XBEE_SERIAL.end();

    delay(1000);

    XBEE_SERIAL.begin(XBEE_SERIAL_BAUD);
    xbeecmd.enter();
}

void initXBee(uint8_t hw_id)
{
    XBEE_SERIAL.begin(XBEE_SERIAL_BAUD);
    xbeecmd.attach(XBEE_SERIAL);

    if (!b_need_xbee_init)
    {
        return; // no communication check

        // // check XBee Connection
        // if (xbeecmd.enter())
        // {
        //     if (xbeecmd.ack())
        //     {
        //         Serial.println("XBee Communication Check OK");
        //         xbeecmd.exit();
        //         return;
        //     }
        //     else
        //     {
        //         Serial.println("XBee Communication ERROR !! (NO ACK)");
        //         b_need_xbee_init = true;
        //     }
        // }
        // else
        // {
        //     Serial.println("XBee Communication ERROR !! (CANNOT ENTER)");
        //     b_need_xbee_init = true;
        // }
    }
    else
    {
        Serial.println("ID has been changed!! Re-Init XBee...");
        b_need_xbee_init = true;
    }

    // XBee initialize process

    XBEE_SERIAL.end();
    XBEE_SERIAL.begin(9600);

    if (!xbeecmd.enter())
    {
        Serial.println("XBee baudrate != 9600. Retry in default baudrate...");
        XBEE_SERIAL.end();
        XBEE_SERIAL.begin(XBEE_SERIAL_BAUD);
        if (!xbeecmd.enter())
        {
            while(1)
            {
                Serial.println("XBee Communication Error!!! Check Connection w/ XBee...");
                static bool led = false;
                digitalWrite(13, led);
                led = !led;
                delay(1000);
            }
        }
    }
    else
    {
        Serial.println("XBee baudrate == 9600. Initilize XBee");
        b_need_xbee_init = true;
    }

    if (b_need_xbee_init) setupXBee(hw_id);

    xbeecmd.firmwareVer();
    xbeecmd.hardwareVer();

    Serial.println("XBee Ready");

    xbeecmd.exit();
}

void setVelocity()
{
#if 0
    unpacker.parse();
    while (unpacker.available())
    {
        if (unpacker.id() == MY_ID)
        {
            whill.move(unpacker.velocityX(), unpacker.velocityY(), unpacker.velocityW());
        }
        unpacker.pop();
    }
#else
    static float vel[3];

    unpacker.parse();

    if (unpacker.isStatusReportChanged())
    {
        uint32_t status_report_duration = unpacker.statusReportDurationMs();

        if (status_report_duration == 0xFFFFFFFF)
        {
            whill.streaming(false, WHILL_INFO_INTERVAL_MS, DATASET::SENSOR_INFO);
        }
        else
        {
            whill.streaming(true, status_report_duration, DATASET::SENSOR_INFO);
        }
    }

    while (unpacker.available())
    {
        if (unpacker.id() == MY_ID)
        {
            vel[0] = unpacker.velocityX();
            vel[1] = unpacker.velocityY();
            vel[2] = unpacker.velocityW();
            unpacker.pop();
            stopwatch.restart();
        }
    }

    if (stopwatch.isRunning() && (stopwatch.ms() < XBEE_COMMAND_TIMEOUT_MS))
    {
        whill.move(vel[0], vel[1], vel[2]);
    }
    else
    {
    }
#endif
}

void sendStatus(WhillOmni::LR lr)
{
    while (whill.available(lr))
    {
        packer.pack(MY_ID, (uint8_t)lr, whill.battery(lr), whill.errorCode(lr));
        XBEE_SERIAL.write(packer.data(), packer.size());

#ifdef DEBUG_INFO
        if      (whill.dataset(lr) == 0) printSpeedProfile(lr);
        else if (whill.dataset(lr) == 1) printSensorInfo(lr);
        else    Serial.println("invalid dataset number");
#endif
        whill.pop(lr);
    }
}

void waitReply(WhillOmni::LR lr, uint16_t wait_ms = 0)
{
    delay(wait_ms);

    whill.parse();
    while(whill.available(lr))
    {
#ifdef DEBUG_INFO
        if      (whill.dataset(lr) == 0) printSpeedProfile(lr);
        else if (whill.dataset(lr) == 1) printSensorInfo(lr);
        else    Serial.println("invalid dataset number");
#endif
        whill.pop(lr);
    }
}


template <typename WireType>
void i2c_Scan(WireType& wire)
{
    Serial.println ();
    Serial.println ("I2C scanner. Scanning ...");
    byte count = 0;

    wire.begin();
    for (byte i = 8; i < 120; i++)
    {
        wire.beginTransmission (i);
        if (wire.endTransmission () == 0)
        {
            Serial.print ("Found address: ");
            Serial.print (i, DEC);
            Serial.print (" (0x");
            Serial.print (i, HEX);
            Serial.println (")");
            count++;
            delay (1);
        }
    }
    Serial.println ("Done.");
    Serial.print ("Found ");
    Serial.print (count, DEC);
    Serial.println (" device(s).");
    Serial.println ("***********");
    Serial.println (" ");
}


#endif // MAIN_HEADER_H
