#include <iostream>
#include <iomanip>
#include <fstream>

#include <TSystem.h>
#include <TApplication.h>
#include <TObjectTable.h>
#include <TSocket.h>

#include "ethernet.h"
#include "vmodican.h"

#include "MEnv.h"
#include "MArgs.h"
#include "MCosy.h"
#include "MTime.h"
#include "MDriveCom.h"

#include "MLogManip.h"

#include "MStarguider.h"

using namespace std;

#define EXPERT

static void StartUpMessage()
{
    gLog << all << endl;

    //                1         2         3         4         5         6
    //       123456789012345678901234567890123456789012345678901234567890
    gLog << "========================================================" << endl;
    gLog << "                         COSY                           " << endl;
    gLog << "          Magic Drive Control System Software           " << endl;
    gLog << "     Compiled with ROOT v" << ROOT_RELEASE << " on <" << __DATE__ << ">" << endl;
    gLog << "========================================================" << endl;
    gLog << endl;
}

static void Usage()
{
    //                1         2         3         4         5         6         7         8
    //       12345678901234567890123456789012345678901234567890123456789012345678901234567890
    gLog << all << endl;
    gLog << "Sorry the usage is:" << endl;
    gLog << " cosy [options]" << endl << endl;
    gLog << " Arguments:" << endl;
    gLog << "   n/a" << endl;
    gLog << endl;
    gLog << " Root Options:" << endl;
    gLog << "   -b                        Batch mode (no graphical output to screen)" << endl<<endl;
    gLog << " Options:" << endl;
    gLog.Usage();
    gLog << "   --debug-env=0             Disable debugging setting resources <default>" << endl;
    gLog << "   --debug-env[=1]           Display untouched resources after program execution" << endl;
    gLog << "   --debug-env=2             Display untouched resources after eventloop setup" << endl;
    gLog << "   --debug-env=3             Debug setting resources from resource file and command line" << endl;
    gLog << "   --debug-mem               Debug memory usage" << endl << endl;
    gLog << "   --debug-threads           Debug threads" << endl << endl;
    gLog << "   --rc=Name:option          Set or overwrite a resource of the resource file." << endl << endl;
    gLog << "   --version, -V             Show startup message with version number" << endl;
    gLog << "   -?, -h, --help            This help" << endl << endl;
}

enum Ports_t {
    kPortTPoint     = 4,
    kPortStarguider = 5,
};

Bool_t SwitchCamera(const TString ip, Ports_t port, Bool_t on)
{
    if (ip.IsNull())
        return kTRUE;

    gLog << all << "- Trying to switch " << (on?"on":"off") << " " << (port==kPortTPoint?"TPoint":"Starguider") << " ccd via " << ip << endl;

    TSocket s(ip, 80);
    if (!s.IsValid())
    {
        gLog << err << "ERROR - Could not connect to " << ip << " to switch " << (on?"on":"off") << " " << (port==kPortTPoint?"TPoint":"Starguider") << " ccd." << endl;
        return kFALSE;
    }

    TString msg = Form("GET /ov.html?cmd=1&p=%d&s=%d HTTP/1.1\r\n", port, on?1:0);

    if (s.SendRaw(msg.Data(), msg.Length()) == -1)
    {
        gLog << err << "ERROR - Could not talk to " << ip << " to switch " << (on?"on":"off") << " " << (port==kPortTPoint?"TPoint":"Starguider") << " ccd." << endl;
        return kFALSE;
    }

    char buf[8192];

    if (s.RecvRaw(buf, 8192) == -1)
      return kFALSE;

    // FIXME: Eval buffer

    gLog << all << "- " << (port==kPortTPoint?"TPoint":"Starguider") << " ccd switched " << (on?"on":"off") << "." << endl;

   return kTRUE;
}


/* ---------------------------------------------------------------------- */
int main(int argc, char **argv)
{
    if (!MARS::CheckRootVer())
        return 0xff;

    MLog::RedirectErrorHandler(MLog::kColor);

    //
    // Evaluate arguments
    //
    MArgs arg(argc, argv);
    gLog.Setup(arg);

    StartUpMessage();

    if (arg.HasOnly("-V") || arg.HasOnly("--version"))
        return 0;

    if (arg.HasOnly("-?") || arg.HasOnly("-h") || arg.HasOnly("--help"))
    {
        Usage();
        return 2;
    }

    const Bool_t  kDebugMem     = arg.HasOnlyAndRemove("--debug-mem");
    const Bool_t  kDebugThreads = arg.HasOnlyAndRemove("--debug-threads");
    const TString kConfig       = arg.GetStringAndRemove("--config=", ".cosyrc"); // ceco
    Int_t  kDebugEnv = arg.HasOnlyAndRemove("--debug-env") ? 1 : 0;
    kDebugEnv = arg.GetIntAndRemove("--debug-env=", kDebugEnv);

    //
    // check for the right usage of the program (number of arguments)
    //
    if (arg.GetNumArguments()>0)
    {
        gLog << warn << "WARNING - Wrong number of arguments..." << endl;
        Usage();
        return 2;
    }

    //
    // Now we access/read the resource file. This will remove all
    // --rc= from the list of arguments.
    //
    MEnv env(kConfig);
    if (!env.IsValid())
    {
        gLog << err << "ERROR - Reading resource file " << kConfig << "." << endl;
        return 0xfe;
    }

    const Int_t   channel     = env.GetValue("DefaultCameraChannel", 0);
    const TString sps         = env.GetValue("IpAddressSPS", "sps");
    const TString ceco        = env.GetValue("IpAddressCentralControl", "161.72.130.2");
    const Int_t   ceco_rx     = env.GetValue("PortCentralControlRX", 7404);
    const Int_t   ceco_tx     = env.GetValue("PortCentralControlTX", 7304);
    const TString powerswitch = env.GetValue("IpAddressPowerSwitch", "");
    const Int_t   telescope   = env.GetValue("Telescope", 1);

    // And move the resource options from the command line to the MEnv
    if (!env.TakeEnv(arg, kDebugEnv>2))
        return 0xfd;

    //
    // check for the right usage of the program (number of options)
    //
    if (arg.GetNumOptions()>0)
    {
        gLog << warn << "WARNING - Unknown commandline options..." << endl;
        arg.Print("options");
        gLog << endl;
        return 2;
    }

    if (!gLog.IsOutputDeviceEnabled(MLog::eFile))
    {
        const TString name = MCosy::GetFileName("log", "cosy", "log");
        gLog << inf << "Open automatic logfile: " << name << endl;
        gLog.SetOutputFile(name);
        gLog.EnableOutputDevice(MLog::eFile);
    }

    gLog << all << "Starting Cosy at " << MTime(-1) << " in thread " << TThread::SelfId() << "..." << endl;

    //
    // start the main window
    //
    gLog << all << "- Initialising Root environment." << endl;

    //
    // Initialize root
    //
    //MArray::Class()->IgnoreTObjectStreamer();
    //MParContainer::Class()->IgnoreTObjectStreamer();

    TApplication app("cosy", &argc, argv);
    if (gROOT->IsBatch())
    {
        gLog << err << "ERROR - Cannot run in Batch mode!" << endl;
        return 0;
    }
    if (!gClient)
    {
        gLog << err << "Bombing... maybe your DISPLAY variable is not set correctly!" << endl;
        return 1;
    }

    if (kDebugMem)
        TObject::SetObjectStat(kTRUE);

    // Check if a connection can be established
    const Int_t save = gDebug;
    gDebug = 1;
    TSocket test(sps, 5357);
    gDebug = save;
    test.Close();

    //
    // Create the Network. Device: /dev/dpm_00, Rate: 500kbps
    //
    gLog << all <<"- Constructing MCosy." << endl;
/*
    //
    // check for the right usage of the program
    //
    int mode = 0;
    if (argc==2 && (argv[1][0]=='-' || argv[1][1]=='m'))
        switch (argv[1][2])
        {
        case '0':      // standard
            mode = 0;
            break;
        case '1':      // SE mode
            mode = 1;
            break;
        case '2':      // GUI demo mode
            mode = 2;
            break;
        }
        */

    MDriveCom *com = new MDriveCom(ceco, ceco_tx, ceco_rx);
    com->SetTelescope(telescope);

    MCosy *cosy = new MCosy(env, com);

    com->SetMsgQueue(cosy);

    Interface *interface = new Ethernet(sps, 5357, cosy);
    // Interface *interface = new VmodIcan(cosy, "/dev/dpm_00", 125);

    gLog << all << "- Starting MCosy." << endl;

    cosy->Start(env);

    gLog << all << "- MCosy started." << endl;

    // FIXME: Is this the right position?
    if (kDebugEnv>0)
        env.PrintUntouched();

    MStarguider *client=0;
    if (channel>=0)
    {
        SwitchCamera(powerswitch, kPortTPoint,     kTRUE);
        SwitchCamera(powerswitch, kPortStarguider, kTRUE);

        gLog << all << "- Starting Starguider." << endl;

        client=new MStarguider(MObservatory::kMagic1, channel);
        client->SetupEnv(env);
        //client.SetDriveCom(&com);
        cosy->SetStarguider(client);
        client->SetCosy(cosy);
    }

    gLog << all << "- Starting mainloop." << endl;

    app.Run(kTRUE);

    if (kDebugThreads)
        TThread::Ps();

    if (channel>=0)
    {
        client->SetCosy(NULL);
        cosy->SetStarguider(NULL);
        gLog << all << "- Stopping starg." << endl;
        delete client;

        SwitchCamera(powerswitch, kPortTPoint,     kFALSE);
        SwitchCamera(powerswitch, kPortStarguider, kFALSE);
    }

    gLog << all << "- Stopping cosy." << endl;
    cosy->Stop();

    gLog <<  all << MTime(-1) << ": MCosy stopped." << endl;

    delete interface;

    gLog << all << "Deleting cosy at " << MTime(-1) << endl;

    //com.SetMsgQueue(NULL);

    delete cosy;

    gLog << all << "Deleting DriveCom at " << MTime(-1) << endl;
    delete com;

    if (kDebugThreads)
        TThread::Ps();

    //delete app;

    if (TObject::GetObjectStat())
    {
        TObject::SetObjectStat(kFALSE);
        gObjectTable->Print();
    }

    gLog << all << "The End." << endl;
}
