/*
 * Copyright (c) 2017, Arm Limited and affiliates.
 * SPDX-License-Identifier: Apache-2.0
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "AT_CellularDevice.h"

using namespace events;
using namespace mbed;

#define DEFAULT_AT_TIMEOUT 1000 // at default timeout in milliseconds

AT_CellularDevice::AT_CellularDevice(EventQueue &queue) :
    _atHandlers(0), _network(0), _sms(0), _sim(0), _power(0), _information(0), _queue(queue),
    _default_timeout(DEFAULT_AT_TIMEOUT), _modem_debug_on(false)
{
}

AT_CellularDevice::~AT_CellularDevice()
{
    close_network();
    close_sms();
    close_power();
    close_sim();
    close_information();

    ATHandler *atHandler = _atHandlers;
    while (atHandler) {
        ATHandler *old = atHandler;
        atHandler = atHandler->_nextATHandler;
        delete old;
    }
}

// each parser is associated with one filehandle (that is UART)
ATHandler *AT_CellularDevice::get_at_handler(FileHandle *fileHandle)
{
    if (!fileHandle) {
        return NULL;
    }
    ATHandler *atHandler = _atHandlers;
    while (atHandler) {
        if (atHandler->get_file_handle() == fileHandle) {
            atHandler->inc_ref_count();
            return atHandler;
        }
        atHandler = atHandler->_nextATHandler;
    }

    atHandler = new ATHandler(fileHandle, _queue, _default_timeout, "\r", get_send_delay());
    if (atHandler) {
        if (_modem_debug_on) {
            atHandler->set_debug(_modem_debug_on);
        }
        atHandler->_nextATHandler = _atHandlers;
        _atHandlers = atHandler;
    }

    return atHandler;
}

void AT_CellularDevice::release_at_handler(ATHandler *at_handler)
{
    if (!at_handler) {
        return;
    }
    at_handler->dec_ref_count();
    if (at_handler->get_ref_count() == 0) {
        // we can delete this at_handler
        ATHandler *atHandler = _atHandlers;
        ATHandler *prev = NULL;
        while (atHandler) {
            if (atHandler == at_handler) {
                if (prev == NULL) {
                    _atHandlers = _atHandlers->_nextATHandler;
                } else {
                    prev->_nextATHandler = atHandler->_nextATHandler;
                }
                delete atHandler;
                break;
            } else {
                prev = atHandler;
                atHandler = atHandler->_nextATHandler;
            }
        }
    }
}

CellularNetwork *AT_CellularDevice::open_network(FileHandle *fh)
{
    if (!_network) {
        ATHandler *atHandler = get_at_handler(fh);
        if (atHandler) {
            _network = new AT_CellularNetwork(*atHandler);
            if (!_network) {
                release_at_handler(atHandler);
            }
        }
    }
    return _network;
}

CellularSMS *AT_CellularDevice::open_sms(FileHandle *fh)
{
    if (!_sms) {
        ATHandler *atHandler = get_at_handler(fh);
        if (atHandler) {
            _sms = new AT_CellularSMS(*atHandler);
            if (!_sms) {
                release_at_handler(atHandler);
            }
        }
    }
    return _sms;
}

CellularSIM *AT_CellularDevice::open_sim(FileHandle *fh)
{
    if (!_sim) {
        ATHandler *atHandler = get_at_handler(fh);
        if (atHandler) {
            _sim = new AT_CellularSIM(*atHandler);
            if (!_sim) {
                release_at_handler(atHandler);
            }
        }
    }
    return _sim;
}

CellularPower *AT_CellularDevice::open_power(FileHandle *fh)
{
    if (!_power) {
        ATHandler *atHandler = get_at_handler(fh);
        if (atHandler) {
            _power = new AT_CellularPower(*atHandler);
            if (!_power) {
                release_at_handler(atHandler);
            }
        }
    }
    return _power;
}

CellularInformation *AT_CellularDevice::open_information(FileHandle *fh)
{
    if (!_information) {
        ATHandler *atHandler = get_at_handler(fh);
        if (atHandler) {
            _information = new AT_CellularInformation(*atHandler);
            if (!_information) {
                release_at_handler(atHandler);
            }
        }
    }
    return _information;
}

void AT_CellularDevice::close_network()
{
    if (_network) {
        ATHandler *atHandler = &_network->get_at_handler();
        delete _network;
        _network = NULL;
        release_at_handler(atHandler);
    }
}

void AT_CellularDevice::close_sms()
{
    if (_sms) {
        release_at_handler(&_sms->get_at_handler());
        delete _sms;
        _sms = NULL;
    }
}
void AT_CellularDevice::close_power()
{
    if (_power) {
        release_at_handler(&_power->get_at_handler());
        delete _power;
        _power = NULL;
    }
}

void AT_CellularDevice::close_sim()
{
    if (_sim) {
        release_at_handler(&_sim->get_at_handler());
        delete _sim;
        _sim = NULL;
    }
}

void AT_CellularDevice::close_information()
{
    if (_information) {
        release_at_handler(&_information->get_at_handler());
        delete _information;
        _information = NULL;
    }
}

void AT_CellularDevice::set_timeout(int timeout)
{
    _default_timeout = timeout;

    ATHandler *atHandler = _atHandlers;
    while (atHandler) {
        atHandler->set_at_timeout(_default_timeout, true); // set as default timeout
        atHandler = atHandler->_nextATHandler;
    }
}

uint16_t AT_CellularDevice::get_send_delay()
{
    return 0;
}

void AT_CellularDevice::modem_debug_on(bool on)
{
    _modem_debug_on = on;

    ATHandler *atHandler = _atHandlers;
    while (atHandler) {
        atHandler->set_debug(_modem_debug_on);
        atHandler = atHandler->_nextATHandler;
    }
}

NetworkStack *AT_CellularDevice::get_stack()
{
    if (!_network) {
        return NULL;
    }
    return _network->get_stack();
}

nsapi_error_t AT_CellularDevice::init_module(FileHandle *fh)
{
    return NSAPI_ERROR_OK;
}
