/*
 * Copyright (C) 2019 by Multi-Tech Systems
 *
 * This file is part of libmts-io.
 *
 * libmts-io is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 2 of the License, or
 * (at your option) any later version.
 *
 * libmts-io 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 Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with libmts-io.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

#include <mts/MTS_IO_CellularRadioFactory.h>
#include <mts/MTS_IO_HE910DRadio.h>
#include <mts/MTS_IO_HE910EUDRadio.h>
#include <mts/MTS_IO_LE910NAGRadio.h>
#include <mts/MTS_IO_LE910C4NFRadio.h>
#include <mts/MTS_IO_LE910NA1Radio.h>
#include <mts/MTS_IO_LE910SVGRadio.h>
#include <mts/MTS_IO_LE910SV1Radio.h>
#include <mts/MTS_IO_LE910EUGRadio.h>
#include <mts/MTS_IO_LE910C4EURadio.h>
#include <mts/MTS_IO_LE910EU1Radio.h>
#include <mts/MTS_IO_LE910C1NSRadio.h>
#include <mts/MTS_IO_LE910C1APRadio.h>
#include <mts/MTS_IO_LE866A1JSRadio.h>
#include <mts/MTS_IO_ME910C1WWRadio.h>
#include <mts/MTS_IO_GE910Radio.h>
#include <mts/MTS_IO_CE910Radio.h>
#include <mts/MTS_IO_DE910Radio.h>
#include "mts/MTS_IO_EG95Radio.h"
#include <mts/MTS_Logger.h>

using namespace MTS::IO;

CellularRadioFactory::CellularRadioFactory() {
    m_mCreationMap[HE910DRadio::MODEL_NAME]       = &CellularRadioFactory::createHE910D;
    m_mCreationMap[HE910EUDRadio::MODEL_NAME]     = &CellularRadioFactory::createHE910EUD;
    m_mCreationMap[LE910NAGRadio::MODEL_NAME]     = &CellularRadioFactory::createLE910NAG;
    m_mCreationMap[LE910C4NFRadio::MODEL_NAME]    = &CellularRadioFactory::createLE910C4NF;
    m_mCreationMap[LE910NA1Radio::MODEL_NAME]     = &CellularRadioFactory::createLE910NA1;
    m_mCreationMap[LE910SVGRadio::MODEL_NAME]     = &CellularRadioFactory::createLE910SVG;
    m_mCreationMap[LE910SV1Radio::MODEL_NAME]     = &CellularRadioFactory::createLE910SV1;
    m_mCreationMap[LE910EUGRadio::MODEL_NAME]     = &CellularRadioFactory::createLE910EUG;
    m_mCreationMap[LE910C4EURadio::MODEL_NAME]    = &CellularRadioFactory::createLE910C4EU;
    m_mCreationMap[LE910EU1Radio::MODEL_NAME]     = &CellularRadioFactory::createLE910EU1;
    m_mCreationMap[LE910C1NSRadio::MODEL_NAME]    = &CellularRadioFactory::createLE910C1NS;
    m_mCreationMap[LE910C1APRadio::MODEL_NAME]    = &CellularRadioFactory::createLE910C1AP;
    m_mCreationMap[ME910C1WWRadio::MODEL_NAME]    = &CellularRadioFactory::createME910C1WW;
    m_mCreationMap[GE910Radio::MODEL_NAME]        = &CellularRadioFactory::createGE910;
    m_mCreationMap[DE910Radio::MODEL_NAME]        = &CellularRadioFactory::createDE910;
    m_mCreationMap[CE910Radio::MODEL_NAME]        = &CellularRadioFactory::createCE910;
    m_mCreationMap[LE866A1JSRadio::MODEL_NAME]    = &CellularRadioFactory::createLE866A1JS;
    m_mCreationMap[EG95Radio::MODEL_NAME]         = &CellularRadioFactory::createEG95Radio;
}

ICellularRadio* CellularRadioFactory::create(const std::string& sModel, const std::string& sPort) {

    std::string model(sModel);

    if(model.size() == 0) {
        model = identifyRadio(sPort);
    }

    if(m_mCreationMap.count(model)) {
        //Call this object's function via a function pointer
        return (this->*m_mCreationMap[model])(sPort);
    }

    printWarning("[CELLULARRADIOFACTORY] Unknown radio [%s].  Attempting HE910 version.", model.c_str());

    return new HE910DRadio(sPort);
}

std::string CellularRadioFactory::identifyRadio(const std::string& sPort) {
    MTS::AutoPtr<Connection> apIo;

    //Attempt to open port to radio
    apIo.reset(new SerialConnection(SerialConnection::Builder(sPort).baudRate(115200).useLockFile().build()));
    while(!apIo->open(30000)) {
        printError("CellularRadioFactory| Failed to open radio port [%s]", sPort.c_str());
        return ICellularRadio::VALUE_UNKNOWN;
    }

    //Attempt basic radio communication
    if(ICellularRadio::test(apIo) != ICellularRadio::SUCCESS) {
        printError("CellularRadioFactory| Failed to communicate with radio on port [%s]", sPort.c_str());
        apIo->close();
        return ICellularRadio::VALUE_UNKNOWN;
    }

    //Get model
    int count = 0;
    std::string sCmd("AT+GMM");
    std::string sResult;
    do {
        sResult = ICellularRadio::sendCommand(apIo, sCmd, ICellularRadio::DEFAULT_BAIL_STRINGS, 1000, ICellularRadio::CR);
        if (sResult.find("OK") == std::string::npos) {
            printDebug("RADIO| Attempting to get radio model [%s] ...", sResult.c_str());
        } else {
            break;
        }
        count++;
    } while (count < 30);

    if(count == 30) {
        printDebug("RADIO| Unable to get radio model");
        apIo->close();
        return ICellularRadio::VALUE_UNKNOWN;
    }

    std::string sModel = ICellularRadio::extractModelFromResult(sResult);
    printDebug("RADIO| Extracted [%s] from AT+GMM query", sModel.c_str());
    apIo->close();
    return sModel;
}

ICellularRadio* CellularRadioFactory::createHE910D(const std::string& sPort) const {
    return new HE910DRadio(sPort);
}

ICellularRadio* CellularRadioFactory::createHE910EUD(const std::string& sPort) const {
    return new HE910EUDRadio(sPort);
}

ICellularRadio* CellularRadioFactory::createLE910NAG(const std::string& sPort) const {
    return new LE910NAGRadio(sPort);
}

ICellularRadio* CellularRadioFactory::createLE910C4NF(const std::string& sPort) const {
    return new LE910C4NFRadio(sPort);
}

ICellularRadio* CellularRadioFactory::createLE910NA1(const std::string& sPort) const {
    return new LE910NA1Radio(sPort);
}

ICellularRadio* CellularRadioFactory::createLE910SVG(const std::string& sPort) const {
    return new LE910SVGRadio(sPort);
}

ICellularRadio *CellularRadioFactory::createLE910SV1(const std::string& sPort) const {
    return new LE910SV1Radio(sPort);
}

ICellularRadio* CellularRadioFactory::createLE910EUG(const std::string& sPort) const {
    return new LE910EUGRadio(sPort);
}

ICellularRadio* CellularRadioFactory::createLE910C4EU(const std::string& sPort) const {
    return new LE910C4EURadio(sPort);
}

ICellularRadio* CellularRadioFactory::createLE910EU1(const std::string& sPort) const {
    return new LE910EU1Radio(sPort);
}

ICellularRadio* CellularRadioFactory::createLE910C1NS(const std::string& sPort) const {
    return new LE910C1NSRadio(sPort);
}

ICellularRadio* CellularRadioFactory::createLE910C1AP(const std::string& sPort) const {
    return new LE910C1APRadio(sPort);
}

ICellularRadio* CellularRadioFactory::createME910C1WW(const std::string& sPort) const {
    return new ME910C1WWRadio(sPort);
}

ICellularRadio* CellularRadioFactory::createGE910(const std::string& sPort) const {
    return new GE910Radio(sPort);
}

ICellularRadio* CellularRadioFactory::createDE910(const std::string& sPort) const {
    return new DE910Radio(sPort);
}

ICellularRadio* CellularRadioFactory::createCE910(const std::string& sPort) const {
    return new CE910Radio(sPort);
}

ICellularRadio* CellularRadioFactory::createLE866A1JS(const std::string &sPort) const {
    return new LE866A1JSRadio(sPort);
}

ICellularRadio* CellularRadioFactory::createEG95Radio(const std::string& sPort) const
{
    return new EG95Radio(sPort);
}