#include "Diagnostics.h"

using namespace PeerSDK;

void PEERSDK_CALLBACK X(void*, DiscoveryMatchEventArgs const& e)
{
    printf("haha\n");
}

DiagnosticTool::DiagnosticTool()
{
    Peer::Startup();

//     DiscoveryService* ds = new DiscoveryService();
//     ds->DiscoveryMatch().Add(NULL, X);
//     ds->Start();
//     ds->ProbeIPCam(IPAddress::Parse("192.168.3.136"));

    m_peer = new Peer();

    m_stream = NULL;
}

DiagnosticTool::~DiagnosticTool()
{
    delete m_peer;

    Peer::Cleanup();
}

void DiagnosticTool::Start(const char* host, int port , const char* user, const char* password)
{
    if (!Phase_Connect(host, port, user, password))
        return;
    WRITE_LOG(L"----------------------------------------------------------------------\n");

    if (!Phase_LiveStream())
        return;
    WRITE_LOG(L"----------------------------------------------------------------------\n");

    if (!Phase_LogList())
        return;
    WRITE_LOG(L"----------------------------------------------------------------------\n");

    if (!Phase_RecordList())
        return;
    WRITE_LOG(L"----------------------------------------------------------------------\n");
}

PeerResult DiagnosticTool::Phase_Connect(const char* host, int port , const char* user, const char* password)
{
    String type;

    WRITE_LOG(L"Connect to %S:%d ...\n", host, port);
    RESULT_CHECK(m_peer->Connect(host, port, user, password, type, true));

    WRITE_LOG(L"    %S is \"%s\"\n", host, m_peer->Type().ToNative());

    return PeerResult();

    PeerRecorder* recorder = m_peer->Recorder();
    RecordScheme s = recorder->DefaultRecordScheme();

    for (int32 i = 0; i < s.Channels().Count(); i++)
    {
        s.Channels()[i].DisableRegularRecord();
    }
    recorder->SetDefaultRecordScheme(s);

    switch (m_peer->OutputVideoFormat())
    {
    case VideoFormat_None:
        WRITE_LOG(L"    Output video format: None (%d)\n", m_peer->OutputVideoFormat());
        break;
    case VideoFormat_NTSC:
        WRITE_LOG(L"    Output video format: NTSC (%d)\n", m_peer->OutputVideoFormat());
        break;
    case VideoFormat_PAL:
        WRITE_LOG(L"    Output video format: PAL (%d)\n", m_peer->OutputVideoFormat());
        break;
    case VideoFormat_720p24:
        WRITE_LOG(L"    Output video format: 720p@24Hz (%d)\n", m_peer->OutputVideoFormat());
        break;
    case VideoFormat_720p25:
        WRITE_LOG(L"    Output video format: 720p@25Hz (%d)\n", m_peer->OutputVideoFormat());
        break;
    case VideoFormat_720p30:
        WRITE_LOG(L"    Output video format: 720p@30Hz (%d)\n", m_peer->OutputVideoFormat());
        break;
    case VideoFormat_720p50:
        WRITE_LOG(L"    Output video format: 720p@50Hz (%d)\n", m_peer->OutputVideoFormat());
        break;
    case VideoFormat_720p60:
        WRITE_LOG(L"    Output video format: 720p@60Hz (%d)\n", m_peer->OutputVideoFormat());
        break;
    case VideoFormat_1080i50:
        WRITE_LOG(L"    Output video format: 1080i@50Hz (%d)\n", m_peer->OutputVideoFormat());
        break;
    case VideoFormat_1080i60:
        WRITE_LOG(L"    Output video format: 1080i@60Hz (%d)\n", m_peer->OutputVideoFormat());
        break;
    case VideoFormat_1080p24:
        WRITE_LOG(L"    Output video format: 1080p@24Hz (%d)\n", m_peer->OutputVideoFormat());
        break;
    case VideoFormat_1080p25:
        WRITE_LOG(L"    Output video format: 1080p@25Hz (%d)\n", m_peer->OutputVideoFormat());
        break;
    case VideoFormat_1080p30:
        WRITE_LOG(L"    Output video format: 1080p@30Hz (%d)\n", m_peer->OutputVideoFormat());
        break;
    case VideoFormat_1080p50:
        WRITE_LOG(L"    Output video format: 1080p@50Hz (%d)\n", m_peer->OutputVideoFormat());
        break;
    case VideoFormat_1080p60:
        WRITE_LOG(L"    Output video format: 1080p@60Hz (%d)\n", m_peer->OutputVideoFormat());
        break;
    }

    switch (m_peer->VideoFormatDetectMethod())
    {
    case VideoFormatDetection_Auto:
        WRITE_LOG(L"    Input video format: auto detect (%d)\n", m_peer->VideoFormatDetectMethod());
        break;
    case VideoFormatDetection_FixedNTSC:
        WRITE_LOG(L"    Input video format: NTSC (%d)\n", m_peer->VideoFormatDetectMethod());
        break;
    case VideoFormatDetection_FixedPAL:
        WRITE_LOG(L"    Input video format: PAL (%d)\n", m_peer->VideoFormatDetectMethod());
        break;
    case VideoFormatDetection_DIP:
        WRITE_LOG(L"    Input video format: DIP switch (%d)\n", m_peer->VideoFormatDetectMethod());
        break;
    }

    WRITE_LOG(L"    %d channels:\n", m_peer->Channels().Count());
    for (int i = 0; i < m_peer->Channels().Count(); i++)
    {
        PeerChannel* channel = m_peer->Channels()[i];
        if (channel->Audio()->IsPresent())
            WRITE_LOG(L"        [CH%02d] /audio/  %s\n", i + 1, channel->Name().ToNative());
        else
            WRITE_LOG(L"        [CH%02d]          %s\n", i + 1, channel->Name().ToNative());
    }
    WRITE_LOG(L"    %d Relay(s)\n", m_peer->Relays().Count());

    PeerHDDList hdds;
    RESULT_CHECK(m_peer->GetHDDList(hdds));
    WRITE_LOG(L"    %d HDDs(s)\n", hdds.Count());
    for (int i = 0; i < hdds.Count(); i++)
    {
        WRITE_LOG(L"        [HDD%d] %lld bytes\n", i + 1, hdds[i].Available());
    }

//     PeerHelper helper;
//     while (true)
//     {
//         Memory snapshot;
//         RESULT_CHECK(helper.GetSystemSnapshot(host, port, user, password, snapshot));
//         WRITE_LOG(L"%S\n", (const char*)&snapshot[0]);
//     }
// 
    return PeerResult();
}

PeerResult DiagnosticTool::Phase_LiveStream()
{
    m_stream = NULL;

    WRITE_LOG(L"Create a live stream ...\n");
    RESULT_CHECK(m_peer->CreateLiveStream(&m_stream));

    WRITE_LOG(L"    %d streams are available, press any key to switch active stream\n", m_stream->Available());

    m_stream->ErrorOccurred().Add(this, &DiagnosticTool::Live_ErrorOccurred);
    m_stream->VideoArrived().Add(this, &DiagnosticTool::Live_VideoArrived);
    m_stream->AudioArrived().Add(this, &DiagnosticTool::Live_AudioArrived);

    m_ifrmaes      = 0;
    m_pfrmaes      = 0;
    m_audiosamples = 0;

    PrintStreamStatus();

    m_stream->Start();

    for (int i = 0; i < m_stream->Available(); i++)
    {
        GETCHAR();
        WRITE_LOG(L"\n");

        m_ifrmaes      = 0;
        m_pfrmaes      = 0;
        m_audiosamples = 0;

        m_stream->SetActive(i + 1);
    }

    WRITE_LOG(L"\n    Dispose the stream\n");
    m_stream->Dispose();

    return PeerResult();
}

PeerResult DiagnosticTool::Phase_LogList()
{
    WRITE_LOG(L"List all system log ...\n");

    PeerLogList loglist;
    RESULT_CHECK(m_peer->GetLogList(loglist));

    WRITE_LOG(L"    Total logs: %d\n", loglist.Count());
    for (int i = 0; i < 10 && i < loglist.Count(); i++)
    {
        PeerLog log = loglist[i];

        String type;
        switch (log.Type())
        {
#define TYPE_TO_STR(t)  case LogType_##t: type = #t; break;
        TYPE_TO_STR(PowerOn)
        TYPE_TO_STR(RecordCH)
        TYPE_TO_STR(VLoss)
        TYPE_TO_STR(Sensor)
        TYPE_TO_STR(Motion)
        TYPE_TO_STR(Login)
        TYPE_TO_STR(Logout)
        TYPE_TO_STR(ConfigExport)
        TYPE_TO_STR(ConfigDefault)
        TYPE_TO_STR(ConfigImport)
        TYPE_TO_STR(LogExport)
        TYPE_TO_STR(LogClear)
        TYPE_TO_STR(ChangeDateTime)
        TYPE_TO_STR(ChangeRecordSetting)
        TYPE_TO_STR(HDDFormat)
        TYPE_TO_STR(HDDSet)
        TYPE_TO_STR(Upgrade)
        TYPE_TO_STR(Backup)
        TYPE_TO_STR(ChangeAdminPass)
        TYPE_TO_STR(NoHDD)
        TYPE_TO_STR(HDDFull)
        TYPE_TO_STR(HDDError)
        TYPE_TO_STR(MCUError)
        TYPE_TO_STR(SystemError)
        TYPE_TO_STR(WriteError)
        TYPE_TO_STR(Reboot)
#undef  TYPE_TO_STR
        }
        if (log.HasChannel())
            WRITE_LOG(L"    %c %04d/%02d/%02d %02d:%02d:%02d [CH%02d] %s\n", log.Playable() ? L'>' : L' ', log.Time().Year(), log.Time().Month(), log.Time().Day(), log.Time().Hour(), log.Time().Minute(), log.Time().Second(), log.Channel() + 1, type.ToNative());
        else
            WRITE_LOG(L"    %c %04d/%02d/%02d %02d:%02d:%02d        %s\n", log.Playable() ? L'>' : L' ', log.Time().Year(), log.Time().Month(), log.Time().Day(), log.Time().Hour(), log.Time().Minute(), log.Time().Second(), type.ToNative());
    }
    WRITE_LOG(L"    ...\n");

    return PeerResult();
}

PeerResult DiagnosticTool::Phase_RecordList()
{
    WRITE_LOG(L"List all record list ...\n");

    PeerRecordList* list = NULL;
    RESULT_CHECK(m_peer->CreateRecordList(&list));

    while (true)
    {
        int year = 0;
        WRITE_LOG(L"    year (0 to exit): ");
        scanf("%d", &year);
        if (year == 0)
            break;

        int month = 0;
        WRITE_LOG(L"    month (0 to exit): ");
        scanf("%d", &month);
        if (month == 0)
            break;

        IntList dayList;
        RESULT_CHECK(list->GetRecordedDaysOfMonth(year, month, dayList));

        if (dayList.Count() == 0)
        {
            WRITE_LOG(L"    no record\n");
            continue;
        }

        WRITE_LOG(L"    %d", dayList[0]);
        for (int i = 1; i < dayList.Count(); i++)
        {
            WRITE_LOG(L" %d", dayList[i]);
        }
        WRITE_LOG(L"\n");

        int day = 0;
        WRITE_LOG(L"    day (0 to continue): ");
        scanf("%d", &day);
        if (day == 0)
            continue;

        TimeSpanList tsList;
        RESULT_CHECK(list->GetRecordedMinutesOfDay(year, month, day, tsList));

        for (int i = 0; i < tsList.Count(); i++)
        {
            TimeSpan ts = tsList[i];
            WRITE_LOG(L" %02d:%02d\n", ts.Hours(), ts.Minutes());
        }
    }

    list->Dispose();

    return PeerResult();
}

void DiagnosticTool::PrintStreamStatus()
{
    WRITE_LOG(L"    [%d] I-frmae: %d, P-frmae: %d, audio: %d, %d bps (limit: %d)", m_stream->Active(), m_ifrmaes, m_pfrmaes, m_audiosamples, m_stream->DownloadRate(), m_stream->DownloadLimit());
}

void DiagnosticTool::UpdateStreamStatus()
{
    WRITE_LOG(L"    \r");
    WRITE_LOG(L"    [%d] I-frmae: %d, P-frmae: %d, audio: %d, %d bps (limit: %d)", m_stream->Active(), m_ifrmaes, m_pfrmaes, m_audiosamples, m_stream->DownloadRate(), m_stream->DownloadLimit());
}

void DiagnosticTool::Live_ErrorOccurred(void* tag, ErrorOccurredEventArgs const& e)
{
}

void DiagnosticTool::Live_VideoArrived(void* tag, VideoArrivedEventArgs const& e)
{
    DiagnosticTool* self = (DiagnosticTool*)tag;
    switch (e.Type())
    {
    case VideoType_H264_IFrame:
        self->m_ifrmaes++;
        break;
    case VideoType_H264_PFrame:
        self->m_pfrmaes++;
        break;
    }
    self->UpdateStreamStatus();
}

void DiagnosticTool::Live_AudioArrived(void* tag, AudioArrivedEventArgs const& e)
{
    DiagnosticTool* self = (DiagnosticTool*)tag;
    self->m_audiosamples++;
    self->UpdateStreamStatus();
}
