/**
 * @file Controller.h
 *
 */

#pragma once

#include "Raym/Raym.h"

#include "keys.h"
#include "ry0/device/TunerFactory.h"

#include "net/HTTPDaemon.h"
#include "net/RTSPRequest.h"

#define VERSION "0.05"
#define REVISION 37

namespace ry0
{
namespace iPTd
{

typedef struct tm TM;
typedef struct sockaddr SOCKADDR;
typedef struct sockaddr_in SOCKADDR_IN;
typedef struct _stat STAT;

class Controller : public Raym::Application,
                   public Raym::TimerDelegate,
                   public NET::HTTPDaemonDelegate
{
private:

    Raym::String *          _system_path;           // システムパス(実行ファイルが配置されているディレクトリ)
    Raym::String *          _props_path;            // プロパティファイルのパス
    Raym::Dictionary *      _props;                 // プロパティ
    Raym::String *          _status_path;           // ステータスファイルのパス
    Raym::Dictionary *      _status;                // ステータス
    Raym::String *          _epgs_path;             // 番組データファイルのパス

    /**
     * EPGデータ
     *
     * Root                 - Dictionary
     *   KEY_SERVICES       - Dictionary
     *     <service_id>     - Array                 // ソート済み
     *       [Item 0]       - Dictionary            // EPGデータ (時間で昇順にソート)
     *         :
     *       [Item X]
     *   <station_name>     - Number                // 局毎のEPG収集時間[秒]
     *
     *   KEY_TEMP_DATA      - Dictionary            // ここから下は保存前に削除し、保存後に再構築する
     *     <service_id>     - Dictionary
     *       KEY_CHANNELS   - Array
     *         [Item 0]     - String
     *           :
     *         [Item X]
     *       KEY_PROGRAMS   - String
     */
    Raym::Dictionary *      _epgs;

    Raym::String *          _store_path;            // 録画データ格納先

    Raym::Dictionary *      _reservations;          // 予約情報
    Raym::String *          _reservations_path;     // 予約情報ファイルのパス
    int                     _reservation_seq_id;    // 予約情報シーケンスID
    int                     _idle_count;            // アイドルカウンタ

    bool                    _initialized;           // 初期化済み
    HMODULE                 _multi2_dll;
    bool                    _cancel_epg_collect_s;  // EPG収集キャンセル(ISDB-S)
    bool                    _cancel_epg_collect_t;  // EPG収集キャンセル(ISDB-T)

    NET::HTTPDaemon *       _httpd;

    Raym::Dictionary *      _streaming_ctrls;       // ストリーミング制御情報格納用

    // 非同期処理用タイマ
    Raym::Timer *           _timer_restart;         // 再開処理用
    Raym::Timer *           _timer_periodic;        // 周期処理用
    Raym::Timer *           _timer_periodic_2;      // 周期処理用２
    Raym::Timer *           _timer_epg_s;           // EPG(ISDB-S)収集用
    Raym::Timer *           _timer_epg_t;           // EPG(ISDB-T)収集用

    Raym::String *          _iptv_m3u8_local;
    Raym::String *          _iptv_m3u8_remote;
    std::string             _xmltv_channels;

    void scanChannel(int tuner);
    void periodic();
    void periodic_2();

    std::string createVideoPath(int tuner);

public: // httpdからアクセス可能なように
    int                             _tunerCount;
    ry0::device::Tuner *            _tuners[ry0::device::MAX_TUNERS];

protected:
    Controller();
    ~Controller();

public:
    static Controller *alloc();

    int restart();
#if 0
    void suspend();
#endif
    bool isIdleState(bool suspend);
    bool canSuspend();
#if 0
    void delaySuspend();
#endif
    // misc
    static void getTimeWithString(Raym::String *str, time_t *time_var);
    static void getTimeWithEPG(Raym::Dictionary *epg, time_t *start, time_t *end);

    // EPG & 予約録画関連
    void collectEPGsForTuner(int tuner, time_t limit);
    bool collectEPGs(ry0::device::Tuner::Type type);
    void removePastEPGs();
    bool reserve(int service_id, int event_id);
    bool reserve(Raym::Dictionary *epg);
    bool reserve(int tuner, Raym::Dictionary *epg);
    bool cancel(int tuner, int reserve_id);
    void updateKeywordsReservation();
    void updateSchedule();

    // タイマ満了IF (from Timer)
    void timerExpired(Raym::Timer *timer, void *userInfo);

    // チューナ制御用IF
    int getChannel(int tuner);
    bool setChannel(int tuner, int channel);

    // プロパティ取得関連
    bool isTunerInitialized(int tuner);
    bool isTunerEnabled(int tuner);
    bool isChannelEnabled(int tuner, int channel);
    Raym::Array *stationInfos(ry0::device::Tuner::Type type);
    Raym::String *stationName(ry0::device::Tuner::Type type, int channel);
    Raym::Array *programsForServices(Raym::Array *services);
    Raym::String *Controller::stationNameForServiceID(Raym::String *service_id);


    // HTTP制御関連
    NET::HTTPResponse *responseWithHTML(NET::HTTPRequest *request, Raym::String *html);
    NET::HTTPResponse *responseWithUTF8Text(NET::HTTPRequest *request, Raym::String *text);
    NET::HTTPResponse *responseByResult(NET::HTTPRequest *request, bool result);
    NET::HTTPResponse *responseForMain(NET::HTTPRequest *request, SOCKADDR_IN *client);
    NET::HTTPResponse *responseForPrograms(NET::HTTPRequest *request, SOCKADDR_IN *client);
    NET::HTTPResponse *responseForReservation(NET::HTTPRequest *request, SOCKADDR_IN *client);
    NET::HTTPResponse *responseForStatus(NET::HTTPRequest *request, SOCKADDR_IN *client);
    NET::HTTPResponse *responseForRegistCGI(NET::HTTPRequest *request, SOCKADDR_IN *client);
    NET::HTTPResponse *responseForCancelCGI(NET::HTTPRequest *request, SOCKADDR_IN *client);
    NET::HTTPResponse *responseForAddKeywordsCGI(NET::HTTPRequest *request, SOCKADDR_IN *client);
    NET::HTTPResponse *responseForModKeywordsCGI(NET::HTTPRequest *request, SOCKADDR_IN *client);
    NET::HTTPResponse *responseForReloadURI(NET::HTTPRequest *request, SOCKADDR_IN *client, const char *uri, int sec = 0);
    NET::HTTPResponse *responseForPlaylist(NET::HTTPRequest *request, SOCKADDR_IN *client);
    NET::HTTPResponse *responseForXmltv(NET::HTTPRequest *request, SOCKADDR_IN *client);
    NET::HTTPResponse *responseForHLSControl(NET::HTTPRequest *request, SOCKADDR_IN *client, int tuner, int service_id, Raym::String *preset);
    NET::HTTPResponse *requestTunerControl(NET::HTTPRequest *request, SOCKADDR_IN *client, int tuner);
    NET::HTTPResponse *requestRTSP(NET::RTSPRequest *request, SOCKET sock, SOCKADDR_IN *client);
    NET::HTTPResponse *request(NET::HTTPRequest *request, SOCKET sock, SOCKADDR_IN *client);
    NET::HTTPRequest *readRequest(SOCKET sock);

    // Windows用IF (from TrayApp)
    int  start();    // エントリポイント
    void systemWillSuspend();
    void systemResumed();
    void detectIdle();
    void detectNonIdle();
    bool canTerminate();
    void debugLog(const char *message);
};

} // iPTd
} // ry0
