#include "posorderpool.h"
#include "preDefine.h"

#include <QMutexLocker>

#include "event/posevent.h"
#include "event/fmapplication.h"

#include <qDebug>
#include <QThread>
#include <QSqlQuery>
#include <QVariantMap>
#include <QSettings>

#include "QsLog.h"
#include "base/DB/fm_database.h"


//互斥读写订单池
QMap<QString, Order_Info> PosOrderPool::s_order_pool;
//互斥读写自提单池索引
QMap<QString, QString> PosOrderPool::s_pick_up_orders;
//写互斥锁
QMutex PosOrderPool::s_mutex;
//已有订单s_pic
QStringList PosOrderPool::s_order_list;
//外卖新单
int PosOrderPool::s_takeout_new = 0;
//外卖制作单
int PosOrderPool::s_takeout_make = 0;
//外卖退单
int PosOrderPool::s_takeout_refund;
//外卖票配送单
int PosOrderPool::s_takeout_send = 0;
//自提新订单
int PosOrderPool::s_self_new = 0;
//自提制作单
int PosOrderPool::s_self_make = 0;
//自提退单
int PosOrderPool::s_self_refund = 0;
//维护以上订单数量的锁
QMutex PosOrderPool::s_order_num_mutex;

PosOrderPool::PosOrderPool(QObject *parent) : QObject(parent)
{
    FMApplication::subscibeEvent(this, PosEvent::s_inset_orderpool);
    FMApplication::subscibeEvent(this, PosEvent::s_change_orderpool);
    FMApplication::subscibeEvent(this, PosEvent::s_location_orderpool);
    FMApplication::subscibeEvent(this, PosEvent::s_pickuporder_remind);

    loadOrders();

    connect(&_timer, &QTimer::timeout, [this] () {

        QLOG_INFO() << QThread::currentThreadId() << "PosOrderPool clean order";

        if(PosOrderPool::s_mutex.tryLock())
        {
            QString confilename = qApp->applicationDirPath() + "/" + CONFIG_NAME;
            int tmptimeout = QSettings(confilename, QSettings::IniFormat).value(INI_DELEORDERTIMER, 12).toInt();

            if(tmptimeout <= 12)
                tmptimeout = 12;

            if(!s_order_pool.empty())
            {
                unsigned int nowtimer = QDateTime::currentDateTime().toTime_t();
                for (auto it = s_order_pool.begin(); it != s_order_pool.end(); )
                {

                    if(it.value().order_change_time > nowtimer && (it.value().order_status == CompleteRefundOrder || it.value().order_status == CompleteOrder))
                    {
                        QVariantMap map;

                        if(it.value().order_data.order_type == TimelyInvite || it.value().order_data.order_type == AppointmentInvite)
                        {
                            s_pick_up_orders.remove(it.value().order_data.code);
                        }

                        map.insert(EVENT_KEY_ORDERID, it.key());
                        map.insert(EVENT_KEY_ORDERSTATUS, (int)(it.value().order_status));
                        OrderObject tmporder = it.value().order_data;
                        UpdateOrderNum(NULL, &tmporder);
                        POSTEVENTTYPE(PosEvent::s_delete_order, map, QVariantMap);
                        s_order_pool.erase(it ++);
                        continue;
                    }

                    if(it.value().order_change_time < nowtimer && nowtimer - it.value().order_change_time > (tmptimeout*60*60))
                    {
                        QVariantMap map;

                        if(it.value().order_data.order_type == TimelyInvite || it.value().order_data.order_type == AppointmentInvite)
                        {
                            s_pick_up_orders.remove(it.value().order_data.code);
                        }

                        map.insert(EVENT_KEY_ORDERID, it.key());
                        map.insert(EVENT_KEY_ORDERSTATUS, it.value().order_status);       
                        OrderObject tmporder = it.value().order_data;
                        UpdateOrderNum(NULL, &tmporder);
                        POSTEVENTTYPE(PosEvent::s_delete_order, map, QVariantMap);
                        s_order_pool.erase(it ++);
                        continue;
                    }

                    ++ it;
                }
            }
            s_mutex.unlock();
        }
        _timer.stop();
        _timer.start(DEFALUE_CHECK_TIME);
    });

    _timer.start(10000);
}

void PosOrderPool::loadOrders()
{
    FMDataBase db(qApp->applicationDirPath() + "/" + DEFAULT_DBNAME);

    QStringList list;

    list.append(DB_KEY_ORDERID);
    list.append(DB_KEY_ORDETRCAHNGETIME);
    list.append(DB_KEY_ORDERWRITETIMR);
    list.append(DB_KEY_ORDERREFUNDSATUS);
    list.append(DB_KEY_ORDERPRINTFSTATUS);
    list.append(DB_KEY_RESERVED1);

    QSqlQuery query;

    db.find(DEFAULT_DBFORM, query, list);

    while(query.next())
    {
        Order_Info info;

        info.order_id = query.value(0).toString();
        info.order_change_time = query.value(1).toInt();
        info.order_write_status = query.value(2).toInt();
        info.order_refund_status = query.value(3).toInt();
        info.order_print_status = query.value(4).toInt();
        info.reserved1 = query.value(5).toString();
        info.order_status = DefaultOrder;
        info.order_refund = DefaultOrder;
        info.order_data.status = DefaultOrder;
        info.order_data.refund_status = DefaultOrder;

        QLOG_DEBUG() << "info : " << info.order_id << "; info.reserved1 : " << info.reserved1;

        s_mutex.lock();
        s_order_pool.insert(info.order_id, info);
        s_order_list.append(info.order_id);
        s_mutex.unlock();
    }
}

void PosOrderPool::removeOrderInfo(QString key)
{
    QMutexLocker loker(&s_mutex);
    s_order_pool.remove(key);
}

void PosOrderPool::InitOrderPool()
{
    static PosOrderPool orderpool;
}

bool PosOrderPool::TryInsertOrder(OrderObject order)
{
    QLOG_DEBUG() << "Store Phone : " << order.store_phone;

    //订单不能为空
    if(order.order_id.isEmpty())
        return false;

    QMutexLocker loker(&s_mutex);

    //订单不存在
    if(!s_order_pool.contains(order.order_id))
    {
        Order_Info info;

        INITORDERINFO(order, info);

        s_order_pool.insert(order.order_id,info);

        if(order.order_type == TimelyInvite || order.order_type == AppointmentInvite)
            s_pick_up_orders.insert(order.code, order.order_id);

        UpdateOrderNum(&order);

        DEFAULTPOSTEVENT(PosEvent::s_change_order, order.order_id);

        if(order.status == FirmOrder && order.refund_status == DefaultOrder)
        {
            if(!(order.order_type == AppointmentTakeout ||
                 order.order_type == AppointmentDining ||
                 order.order_type == AppointmentInvite))
            {
                QVariantMap map;

                map.insert(EVENT_KEY_ORDERID, order.order_id);
                map.insert(EVENT_KEY_MSG, DEFAULT_LOCATION_PRINT);

                POSTEVENTTYPE(PosEvent::s_order_location, map, QVariantMap);
            }
        }

        return true;
    }

    //订单存在
    Order_Info oldinfo = s_order_pool[order.order_id];

    Order_Info info;

    QLOG_DEBUG() << "$$$$$$$$$$$$$$$$$$$$$$$$$$$" << info.order_id << info.order_refund;
    QLOG_DEBUG() << "$$$$$$$$$$$$$$$$$$$$$$$$$$$" << info.order_id << info.order_status;


    UPDATEORDERINFO(oldinfo, order, info);

    QLOG_DEBUG() << "@@@@@@@@@@@@@@@@@@@@@@@@@@@" <<  info.order_id << info.order_refund;
    QLOG_DEBUG() << "@@@@@@@@@@@@@@@@@@@@@@@@@@@" <<  info.order_id << info.order_status;


    if(oldinfo.order_status != order.status || oldinfo.order_refund != order.refund_status)
    {
        if(order.order_type == TimelyInvite || order.order_type == AppointmentInvite)
            s_pick_up_orders.insert(order.code, order.order_id);

        s_order_pool.insert(order.order_id, info);

        UpdateOrderNum(&info.order_data, &oldinfo.order_data);

        DEFAULTPOSTEVENT(PosEvent::s_change_order, order.order_id);

        if(order.status == FirmOrder && order.refund_status == DefaultOrder && info.order_print_status == 0)
        {
            if(!(order.order_type == AppointmentTakeout ||
                 order.order_type == AppointmentDining ||
                 order.order_type == AppointmentInvite))
            {
                QVariantMap map;

                map.insert(EVENT_KEY_ORDERID, order.order_id);
                map.insert(EVENT_KEY_MSG, DEFAULT_LOCATION_PRINT);

                POSTEVENTTYPE(PosEvent::s_order_location, map, QVariantMap);
            }
        }

        return true;
    }
    else
    {
        s_order_pool.insert(order.order_id, info);
        return true;
    }

    return false;
}

bool PosOrderPool::TryChangeOrderStatus(QString key, int order_status, int refundstatus)
{
    if(order_status >= DefaultOrder && order_status <= CompleteOrder)
    {
        QMutexLocker loker(&s_mutex);

        if(!s_order_pool.contains(key))
            return false;

        Order_Info tmpinfo = s_order_pool[key];
        Order_Info info = s_order_pool[key];

        if(info.order_status != order_status || info.order_refund != refundstatus)
        {
            if(info.order_status != order_status)
            {
                info.order_status = (Order_Status)order_status;
                info.order_data.status = order_status;
            }

            if(info.order_refund != refundstatus)
            {
                info.order_refund = (Order_Status)refundstatus;
                info.order_data.refund_status = refundstatus;
            }

            QLOG_DEBUG() << "+++++++++++++++++++++++++++" << info.order_refund;
            QLOG_DEBUG() << "+++++++++++++++++++++++++++" << info.order_status;
            QLOG_DEBUG() << "+++++++++++++++++++++++++++" << order_status << refundstatus;


            info.order_change_time = QDateTime::currentDateTime().toTime_t();
            s_order_pool.insert(key, info);

            UpdateOrderNum(&info.order_data, &tmpinfo.order_data);

            DEFAULTPOSTEVENT(PosEvent::s_change_order, key);

            if(info.order_status == FirmOrder  && info.order_refund == DefaultOrder && info.order_print_status == 0)
            {
                if(!(info.order_data.order_type == AppointmentTakeout ||
                     info.order_data.order_type == AppointmentDining ||
                     info.order_data.order_type == AppointmentInvite))
                {
                    QVariantMap map;

                    map.insert(EVENT_KEY_ORDERID, info.order_id);
                    map.insert(EVENT_KEY_MSG, DEFAULT_LOCATION_PRINT);

                    POSTEVENTTYPE(PosEvent::s_order_location, map, QVariantMap);
                }
            }

            return true;
        }
    }
    return false;
}

bool PosOrderPool::TryChangeOrderWriteStatus(QString key, int order_write_status)
{
    QMutexLocker loker(&s_mutex);

    if(!s_order_pool.contains(key))
        return false;

    Order_Info info = s_order_pool[key];

    if(info.order_write_status != order_write_status && order_write_status > info.order_write_status)
    {
        info.order_write_status = order_write_status;
        s_order_pool.insert(key, info);

        DEFAULTPOSTEVENT(PosEvent::s_opt_change_order, key);

        return true;
    }
    return false;
}

bool PosOrderPool::TryChangeOrderRefundStatus(QString key, int order_refund_status)
{
    QMutexLocker loker(&s_mutex);

    if(!s_order_pool.contains(key))
        return false;

    Order_Info info = s_order_pool[key];

    if(info.order_refund_status != order_refund_status && order_refund_status > info.order_refund_status)
    {
        info.order_refund_status = order_refund_status;
        s_order_pool.insert(key, info);

        DEFAULTPOSTEVENT(PosEvent::s_opt_change_order, key);

        return true;
    }
    return false;
}

bool PosOrderPool::GetOrderObject(QString key, OrderObject &order)
{
    QMutexLocker loker(&s_mutex);

    if(!s_order_pool.contains(key))
        return false;

    Order_Info info = s_order_pool[key];

    order = info.order_data;

    return true;
}

bool PosOrderPool::GetOrderStatus(QString key, int &status, int &refund_status, bool &oldorder)
{
    QMutexLocker loker(&s_mutex);

    if(!s_order_pool.contains(key))
        return false;

    Order_Info info = s_order_pool[key];

    status = info.order_status;
    refund_status = info.order_refund;

    QLOG_DEBUG() << "input0 : " << info.order_id << info.order_status << info.order_refund << info.order_data.refund_status;

    if(s_order_list.contains(info.order_id, Qt::CaseInsensitive))
        oldorder = true;
    else
        oldorder = false;

    return true;
}

bool PosOrderPool::GetOrderStatusAndOrderType(QString key, int &status, int &refund_status, int &order_type, bool &oldorder)
{
    QMutexLocker loker(&s_mutex);

    if(!s_order_pool.contains(key))
        return false;

    Order_Info info = s_order_pool[key];

    status = info.order_status;
    refund_status = info.order_refund;
    order_type = info.order_data.order_type;

    QLOG_DEBUG() << "input0 : " << info.order_id << info.order_status << info.order_refund << info.order_data.refund_status;

    if(s_order_list.contains(info.order_id, Qt::CaseInsensitive))
        oldorder = true;
    else
        oldorder = false;

    return true;
}

bool PosOrderPool::TryChangeOrderPrintStatus(QString key, int order_print_status)
{
    QMutexLocker loker(&s_mutex);

    if(!s_order_pool.contains(key))
        return false;

    Order_Info info = s_order_pool[key];

    if(info.order_print_status != order_print_status && order_print_status > info.order_print_status)
    {
        info.order_print_status = order_print_status;
        s_order_pool.insert(key, info);

        DEFAULTPOSTEVENT(PosEvent::s_opt_change_order, key);
    }
    return true;
}

bool PosOrderPool::TryChangeOrderRemindStatus(QString key, int order_remind_status)
{
    QMutexLocker loker(&s_mutex);

    if(!s_order_pool.contains(key))
        return false;

    Order_Info info = s_order_pool[key];

    if(info.reserved1.isEmpty() || info.reserved1.toInt() != order_remind_status)
    {
        info.reserved1 = QString::number(order_remind_status);
        s_order_pool.insert(key, info);

        DEFAULTPOSTEVENT(PosEvent::s_opt_change_order, key);
    }
    return true;
}

bool PosOrderPool::GetOrderBaseInfo(QString key, QVariantHash &hash)
{
    QMutexLocker loker(&s_mutex);

    if(!s_order_pool.contains(key))
        return false;

    Order_Info info = s_order_pool[key];

    hash.insert(DB_KEY_ORDERID, info.order_id);
    hash.insert(DB_KEY_ORDETRCAHNGETIME, info.order_change_time);
    hash.insert(DB_KEY_ORDERWRITETIMR, info.order_write_status);
    hash.insert(DB_KEY_ORDERREFUNDSATUS, info.order_refund_status);
    hash.insert(DB_KEY_ORDERPRINTFSTATUS, info.order_print_status);
    hash.insert(DB_KEY_RESERVED1, info.reserved1);
    hash.insert(DB_KEY_RESERVED2, info.reserved2);
    hash.insert(DB_KEY_RESERVED3, info.reserved3);
    hash.insert(DB_KEY_RESERVED4, info.reserved4);
    hash.insert(DB_KEY_RESERVED5, info.reserved5);

    return true;
}

void PosOrderPool::GetOrderNum(int &tk_new, int &tk_make, int &tk_send, int &tk_refund, int &self_new, int &self_make, int &self_refund)
{
    tk_new = s_takeout_new;
    tk_make = s_takeout_make;
    tk_send = s_takeout_send;
    tk_refund = s_takeout_refund;
    self_new = s_self_new;
    self_make = s_self_make;
    self_refund = s_self_refund;
}

bool PosOrderPool::GetSearchOrder(QString key, QMap<QString, QString> &map)
{
    bool serachflag = false;

    if(PosOrderPool::s_mutex.tryLock())
    {
        if(!s_order_pool.empty())
        {
            for (auto it = s_order_pool.begin(); it != s_order_pool.end(); ++ it)
            {
                if(it.value().order_status == DefaultOrder && it.value().order_refund == DefaultOrder)
                    continue;

                QString phone = it.value().order_data.phone;

                if(it.key().startsWith(key, Qt::CaseInsensitive)|| phone.startsWith(key, Qt::CaseInsensitive))
                {
                    map.insert(it.key(), phone);
                    serachflag = true;
                }
            }
        }
        s_mutex.unlock();
        return serachflag;
    }
    return false;
}

bool PosOrderPool::GetOrderStatusWithPickUpCode(QString pickupkey, int &status, int &refund_status)
{
    QMutexLocker loker(&s_mutex);

    if(!s_pick_up_orders.contains(pickupkey))
        return false;

    QString orderid = s_pick_up_orders[pickupkey];

    if(!s_order_pool.contains(orderid))
        return false;

    Order_Info info = s_order_pool[orderid];

//    QLOG_DEBUG() << "input0" << info.order_status;
//    QLOG_DEBUG() << "input1" << info.order_refund;
//    QLOG_DEBUG() << "input2" << info.order_data.status;
//    QLOG_DEBUG() << "input3" << info.order_data.refund_status;

    status = info.order_status;
    refund_status = info.order_refund;

    return true;
}

bool PosOrderPool::GetOrderObjectWithPickUpCode(QString pickupkey, OrderObject &order)
{
    QMutexLocker loker(&s_mutex);

    if(!s_pick_up_orders.contains(pickupkey))
        return false;

    QString orderid = s_pick_up_orders[pickupkey];

    if(!s_order_pool.contains(orderid))
        return false;

    Order_Info info = s_order_pool[orderid];

    order = info.order_data;

    return true;
}

bool PosOrderPool::GetPickUpOrderPrtAndRemid(QString pickupkey, int &prtstatus, int &remindstatus)
{
    QMutexLocker loker(&s_mutex);

    if(!s_order_pool.contains(pickupkey))
        return false;

    Order_Info info = s_order_pool[pickupkey];
    int order_type = info.order_data.order_type;

    if(!(order_type == AppointmentTakeout || order_type == AppointmentDining || order_type == AppointmentInvite))
        return false;

    prtstatus = info.order_print_status;

    if(info.reserved1.isEmpty())
        remindstatus = 0;
    else
        remindstatus = info.reserved1.toInt();

    return true;
}

bool PosOrderPool::event(QEvent *e)
{

    if(e->type() == PosEvent::s_inset_orderpool)
    {
        QLOG_DEBUG() << "PosEvent::s_inset_orderpool : " << QThread::currentThreadId();

        OrderObject order(this);

        GETEVENTINFO(order, e, OrderObject);

        PosOrderPool::TryInsertOrder(order);

        return true;
    }

    if(e->type() == PosEvent::s_pickuporder_remind)
    {
        QLOG_INFO() << "PosEvent::s_change_orderpool : " << QThread::currentThreadId();

        QString orderid;

        GETEVENTINFO(orderid,e,QString);

        TryChangeOrderRemindStatus(orderid, 1);

        return true;
    }

    if(e->type() == PosEvent::s_change_orderpool)
    {
        QLOG_INFO() << "PosEvent::s_change_orderpool : " << QThread::currentThreadId();

        QVariantMap info;

        GETEVENTINFO(info,e,QVariantMap);

        if(!info.contains(EVENT_KEY_ORDERID) || !info.contains(EVENT_KEY_MSG))
            return true;

        if(info.contains(EVENT_KEY_STATUS))
            PosOrderPool::TryChangeOrderStatus(info[EVENT_KEY_ORDERID].toString(), info[EVENT_KEY_MSG].toInt(), info[EVENT_KEY_STATUS].toInt());
        else
            PosOrderPool::TryChangeOrderStatus(info[EVENT_KEY_ORDERID].toString(), info[EVENT_KEY_MSG].toInt());

        return true;
    }

    if(e->type() == PosEvent::s_location_orderpool)
    {
        QLOG_INFO() << "PosEvent::s_location_orderpool" << QThread::currentThreadId();

        QVariantMap info;

        GETEVENTINFO(info,e,QVariantMap);

        if(!info.contains(EVENT_KEY_ORDERID) || !info.contains(EVENT_KEY_MSG) || !info.contains(EVENT_KEY_STATUS))
            return true;

        switch (info[EVENT_KEY_MSG].toInt()) {
        case DEFAULT_LOCATION_PRINT:
            PosOrderPool::TryChangeOrderPrintStatus(info[EVENT_KEY_ORDERID].toString(), info[EVENT_KEY_STATUS].toInt());
            break;
        case DEFAULT_LOCATION_write:
            PosOrderPool::TryChangeOrderWriteStatus(info[EVENT_KEY_ORDERID].toString(), info[EVENT_KEY_STATUS].toInt());
            break;
        case DEFAULT_LOCATION_refund:
            PosOrderPool::TryChangeOrderRefundStatus(info[EVENT_KEY_ORDERID].toString(), info[EVENT_KEY_STATUS].toInt());
            break;
        default:
            break;
        }

        return true;
    }
    return QObject::event(e);
}

void PosOrderPool::GetOrderStatus(const OrderObject *order, int &status)
{
    if(order->refund_status == ApplicationRefundOrder ||
            order->refund_status == ApplicationPartialRefundOrder ||
            order->refund_status == CompleteRefundOrder)
        status = order->refund_status;
    else
        status = order->status;
}

void PosOrderPool::UpdateOrderNum(const OrderObject *order, const OrderObject *orderold)
{
    QMutexLocker loker(&s_order_num_mutex);

    if(orderold != NULL)
    {
        int status = DefaultOrder;

        GetOrderStatus(orderold, status);

        QLOG_DEBUG() << "OLD UpdateOrderNum input 0 : " << orderold->order_id;

        if(orderold->order_type == TimelyTakeout || orderold->order_type == AppointmentTakeout)
        {
            QLOG_DEBUG() << "OLD DECTAKEOUT UpdateOrderNum input 0 : " << orderold->order_id;

            DECTAKEOUT(status, s_takeout_new, s_takeout_make, s_takeout_send, s_takeout_refund);
        }
        else if(orderold->order_type == TimelyInvite || orderold->order_type == AppointmentInvite)
        {
            QLOG_DEBUG() << "OLD DECTAKEOUT UpdateOrderNum input 0 : " << orderold->order_id;
            DECSELF(status, s_self_new, s_self_make, s_self_refund);
        }

    }

    if(order != NULL)
    {
        int status = DefaultOrder;

        GetOrderStatus(order, status);

         QLOG_DEBUG() << "NEW UpdateOrderNum input 0 : " << order->order_id;

        if(order->order_type == TimelyTakeout || order->order_type == AppointmentTakeout)
        {
            QLOG_DEBUG() << "NEW INCTAKEOUT UpdateOrderNum input 0 : " << order->order_id;
            QLOG_DEBUG() << "NEW INCTAKEOUT UpdateOrderNum input 3 : " << status;
            INCTAKEOUT(status, s_takeout_new, s_takeout_make, s_takeout_send, s_takeout_refund);
            QLOG_DEBUG() << "NEW INCTAKEOUT UpdateOrderNum input 4 : " << s_takeout_refund;
        }
        else if(order->order_type == TimelyInvite || order->order_type == AppointmentInvite)
        {
            QLOG_DEBUG() << "NEW INCSELF UpdateOrderNum input 0 : " << order->order_id;
            INCSELF(status, s_self_new, s_self_make, s_self_refund);
        }
    }
}



