package org.freertr.prt;

import java.io.File;
import java.util.ArrayList;
import java.util.List;
import org.freertr.ifc.ifcThread;
import org.freertr.pipe.pipeDiscard;
import org.freertr.pipe.pipeLine;
import org.freertr.pipe.pipeShell;
import org.freertr.user.userFlash;
import org.freertr.user.userFormat;
import org.freertr.util.bits;
import org.freertr.util.cmds;
import org.freertr.util.debugger;
import org.freertr.util.logger;

/**
 * watchdog implementation for microcontroller of stanci
 *
 * @author matecsaba
 */
public class prtWatch implements Runnable {

    private String name;

    private ifcThread thrd;

    private final int ports;

    private String[] names;

    private pipeShell[] procs;

    private static List<prtWatch> dogList = new ArrayList<prtWatch>();

    private int lastTry = -1;

    private int portStates = 0;

    public String toString() {
        return "watchdog " + name;
    }

    /**
     * start watchdog
     *
     * @param name name of interface
     * @param thrd interface thread handler
     * @param par parameters
     */
    public static void ifcAdd(String name, ifcThread thrd, String par) {
        dogList.add(new prtWatch(name, thrd, par));
    }

    /**
     * generate show output
     *
     * @return output
     */
    public static userFormat doShow() {
        userFormat l = new userFormat("|", "prt|nam|run");
        for (int i = 0; i < dogList.size(); i++) {
            dogList.get(i).doShow(l);
        }
        return l;
    }

    /**
     * terminate the watchdogs
     */
    public static void doShut() {
        for (int o = 0; o < dogList.size(); o++) {
            prtWatch l = dogList.get(o);
            for (int i = 0; i < l.ports; i++) {
                l.doShut(i);
            }
            l.sendMagic(0x66);
        }
    }

    /**
     * do clear command
     *
     * @param cmd command
     */
    public static void doClear(cmds cmd) {
        int i = bits.str2num(cmd.word());
        if (i < 0) {
            return;
        }
        if (i >= dogList.size()) {
            return;
        }
        prtWatch dog = dogList.get(i);
        dog.doClear(bits.str2num(cmd.word()));
    }

    /**
     * start watchdog
     *
     * @param nam name of interface
     * @param thr interface thread handler
     * @param par parameters
     */
    public prtWatch(String nam, ifcThread thr, String par) {
        name = nam;
        thrd = thr;
        logger.startThread(this);
        ports = bits.str2num(par);
        names = new String[ports];
        procs = new pipeShell[ports];
    }

    private void doShow(userFormat l) {
        for (int i = 0; i < ports; i++) {
            l.add(i + "|" + names[i] + "|" + isRunning(i));
        }
    }

    private void doClear(int prt) {
        sendPortStat(prt, false);
        lastTry = prt;
    }

    private void doShut(int prt) {
        sendPortStat(prt, false);
        userFlash.delete(names[prt]);
        names[prt] = null;
        procs[prt] = null;
    }

    private void sendMagic(int val) {
        byte[] buf = new byte[1];
        buf[0] = (byte) val;
        try {
            thrd.txOnePack(buf, 0, 1);
            if (debugger.prtWatchEvnt) {
                logger.debug("command (" + bits.byteDump(buf, 0, -1) + ") sent");
            }
            bits.sleep(1000);
        } catch (Exception e) {
            logger.traceback(e);
        }
    }

    private void sendDogUp() {
        if (debugger.prtWatchEvnt) {
            logger.debug("dog feed time");
        }
        sendMagic(0x72);
    }

    private void sendPortStat(int port, boolean stat) {
        if (debugger.prtWatchEvnt) {
            logger.debug("turn port=" + port + " stat=" + stat);
        }
        if (stat) {
            portStates |= bits.bitVals[port];
        } else {
            portStates &= 0xfff - bits.bitVals[port];
        }
        sendMagic(0x40 + portStates);
    }

    private pipeShell doExec(String cmd, boolean wait) {
        if (debugger.prtWatchEvnt) {
            logger.debug("shell (" + cmd + ") started");
        }
        pipeLine pl = new pipeLine(65536, false);
        pipeDiscard.discard(pl.getSide());
        pipeShell sh = pipeShell.exec(pl.getSide(), cmd, null, true, true, false, true);
        if (sh == null) {
            return null;
        }
        if (!wait) {
            if (debugger.prtWatchEvnt) {
                logger.debug("shell (" + cmd + ") is running");
            }
            return sh;
        }
        sh.waitFor();
        if (debugger.prtWatchEvnt) {
            logger.debug("shell (" + cmd + ") done");
        }
        return null;
    }

    private void doPort(int prt) {
        if (debugger.prtWatchEvnt) {
            logger.debug("restarting port " + prt);
        }
        userFlash.delete(names[prt]);
        procs[prt] = null;
        names[prt] = null;
        File[] f1 = userFlash.dirList("/dev/");
        sendPortStat(prt, false);
        bits.sleep(5000);
        sendDogUp();
        sendPortStat(prt, true);
        bits.sleep(15000);
        sendDogUp();
        doCheck(prt);
        doExec("./modeswitch.sh", true);
        bits.sleep(15000);
        doCheck(prt);
        doExec("/bin/mdev -s", true);
        File[] f2 = userFlash.dirList("/dev/");
        String s = null;
        for (int i = 0; i < f2.length; i++) {
            String a = f2[i].getName();
            if (!a.startsWith("tty")) {
                continue;
            }
            int p = -1;
            for (int o = 0; o < f1.length; o++) {
                if (a.equals(f1[o].getName())) {
                    p = o;
                }
            }
            if (p >= 0) {
                continue;
            }
            s = f2[i].getAbsolutePath();
            break;
        }
        if (debugger.prtWatchEvnt) {
            logger.debug("found device " + s);
        }
        if (s == null) {
            return;
        }
        names[prt] = s;
        int p = (prt * 2) + 20051;
        procs[prt] = doExec("./modem.sh " + s + " ./modem.ser" + prt
                + " " + (p + 1) + " " + p, false);
    }

    private void doCheck(int prt) {
        for (int i = 0; i < names.length; i++) {
            if (i == prt) {
                continue;
            }
            if (isRunning(i)) {
                continue;
            }
            doShut(i);
        }
    }

    /**
     * check if port running
     *
     * @param prt port number
     * @return running or not
     */
    public boolean isRunning(int prt) {
        if (procs[prt] == null) {
            return false;
        }
        return procs[prt].isRunning();
    }

    public void run() {
        logger.info("starting watchdog process");
        sendDogUp();
        for (int i = 0; i < names.length; i++) {
            doShut(i);
        }
        for (;;) {
            bits.sleep(15000);
            sendDogUp();
            doCheck(-1);
            lastTry = (lastTry + 1) % names.length;
            if (procs[lastTry] != null) {
                if (procs[lastTry].isRunning()) {
                    continue;
                }
            }
            try {
                doPort(lastTry);
            } catch (Exception e) {
                logger.traceback(e);
            }
            if (isRunning(lastTry) == false) {
                doShut(lastTry);
            }
            sendDogUp();
        }
    }

}
