/*
 * icetft plugin for VDR
 *
 * IcetftSwitch.c - class for switching RX and DTR states at ttyS?
 *
 *
 * This code is distributed under the terms and conditions of the
 * GNU GENERAL PUBLIC LICENSE. See the file COPYING for details.
 */

#include "IcetftSwitch.h"
#include "IcetftStatusMonitor.h"
#include "setup.h"

cIcetftSwitch * IcetftSwitch;

cIcetftSwitch::cIcetftSwitch()
{
    // Initialize any member variables here.
    active=false;
    fd = open(IcetftSetup.device, O_RDWR | O_NOCTTY| O_NDELAY|O_NONBLOCK);

    // test device
    if (fd < 0)
    {
        esyslog("icetft: unable to open %s: %s\n", IcetftSetup.device, strerror(errno));
        return;
    }

    // test & initialize interface
    state=0;
    if (ioctl(fd, TIOCMSET, &state)<0)
    {
        esyslog("icetft: %s: ioctl TIOCMSET failed: %s\n", IcetftSetup.device, strerror(errno));
        return;
    }
    tftState=error;
    tftAction=error;

    timeLastOff=0;
    timeLastOn=0;
    elapsedLastStatus= 0;
    elapsedLastOff = 0;
    elapsedLastOn = 0;
    poweroffTime=(IcetftSetup.poweroffTime*1000);

    // Start Status Monitor
    IcetftStatusMonitor = new cIcetftStatusMonitor;
    Start();
}


cIcetftSwitch::~cIcetftSwitch()
{
    // Clean up after yourself!
    active = false;
    if (IcetftSetup.stateAtShutdown)
        IcetftSwitch->Off();
    delete IcetftStatusMonitor;
    close(fd);
    Cancel(3);
}


void cIcetftSwitch::Action()
{
    dsyslog("IcetftSwitch thread started (pid=%d)", getpid());
    active = true;

    while (active && Running())
    {
        LOCK_THREAD;
        usleep (THREAD_SLEEP_TIME);

        if (tftAction==toggle)
        {
            IcetftSwitch->Toggle();
            tftAction=error;
            continue;
        }
        if (tftAction==on)
        {
            IcetftSwitch->On();
            tftAction=error;
            continue;
        }

        if (tftAction==off)
        {
            IcetftSwitch->Off();
            tftAction=error;
            continue;
        }

        uint64_t current = TimeMs();
        /*
        Due to the cast form "uint64_t" to "unsigned long int" a buffer overflow can happen after
        49,7 days with no status change e.g. osd activity. This schould not really be a problem ;)
        */
        elapsedLastStatus= current-IcetftStatusMonitor->Changed();
        elapsedLastOff = current-timeLastOff;
        elapsedLastOn = current-timeLastOn;

        if(IcetftSetup.wakeup)
            if ( (tftState==off)&& (elapsedLastStatus < WAKEUP_TIMEOUT)&& (elapsedLastOff > LAST_OFF_TIMEOUT) )
        {
             #ifdef DEBUG
                 fprintf(stderr,"icetft: Wakeup timeout %li ms since last OSD activity and %li ms since last off\n", elapsedLastStatus, elapsedLastOff);
            #endif 
            On();
            continue;
        }

        if (IcetftSetup.poweroff)
            if( (tftState==on) && (elapsedLastStatus > poweroffTime) && (elapsedLastOn > poweroffTime) )
        {
            #ifdef DEBUG
                fprintf(stderr,"icetft: Standby timeout %li ms since last status change and %li ms since last on\n", elapsedLastStatus, elapsedLastOn);
            #endif
            Off();
            continue;
        }
    }

    dsyslog("IcetftSwitch thread ended (pid=%d)", getpid());
}


tState cIcetftSwitch::Off()
{
    state |= TFT_OFF;
    // set RTS or DTR
    if (ioctl(fd, TIOCMSET, &state)<0)
    {
        esyslog("icetft: %s: ioctl TIOCMSET failed: %s\n", IcetftSetup.device, strerror(errno));
        return error;
    }

    //tft needs a certain time for switching
    usleep(TFT_SLEEP_TIME);
    #ifdef DEBUG
        fprintf(stderr,"icetft: tft off\n");
    #endif 
    tftState = off;
    timeLastOff= TimeMs();

    if (Reset())
        return off;
    else
        return error;
}


tState cIcetftSwitch::On()
{
    state |= TFT_ON;
    // set RTS or DTR
    if (ioctl(fd, TIOCMSET, &state)<0)
    {
        esyslog("icetft: %s: ioctl TIOCMSET failed: %s\n", IcetftSetup.device, strerror(errno));
        return error;
    }
    // tft needs a certain time for switching
    usleep(TFT_SLEEP_TIME);
    #ifdef DEBUG
        fprintf(stderr,"icetft: tft on \n");
    #endif 
    tftState=on;
    timeLastOn= TimeMs();

    if (Reset())
        return on;
    else
        return error;
}


tState cIcetftSwitch::Toggle()
{
    if (tftState==on)
    {
        return Off();
    }
    else
    {
        return On();
    }
}


tState cIcetftSwitch::getTftState()
{
    while (tftAction!=error)
        usleep (TFT_SLEEP_TIME);
    return tftState;
}


void cIcetftSwitch::setAction(tState action)
{
    tftAction=action;
    return;
}


bool cIcetftSwitch::Reset()
{
    // reset status
    state = 0;
    if (ioctl(fd, TIOCMSET, &state)<0)
    {
        esyslog("icetft: %s: ioctl TIOCMSET failed: %s\n", IcetftSetup.device, strerror(errno));
        return false;
    }
    return true;
}
