#include "BillsManger.h"
#include "Util/IniDataManger.h"
#include "Util/ReqDataManger.h"
#include "QsLog.h"
//#include <QJsonDocument>
#include "NotifyForm.h"
#include "PreDefined.h"
#include "Util/Tools.h"
#include "../fmTakeaway_db/FmTakeaway_db.h"
#include "../fmTakeaway_printer/Fmtakeaway_printer.h"
#include <QFile>
#include <QFileInfo>
#include <QApplication>

#include "Util/wbillcontrol.h"
#include "DClasses/Database.h"

BillsManger &BillsManger::Instance()
{
    static BillsManger billMgr;
    return billMgr;
}

void BillsManger::Login()
{
    //    QJsonObject loginObj = OBJREF(ReqDataManger).GetLoginData();
    QByteArray loginObj = OBJREF(ReqDataManger).GetLoginData();
    QLOG_INFO() << QString("login [data->%1].").arg(QString(loginObj));
    m_loginSocket.getNewJosn(Socket::Login, loginObj);
    return;
}

void BillsManger::_ProcLoginReply(const QByteArray &data)
{
    //QJson::Parser p;
    //bool ok = false;
    QJsonParseError ok;
    //QVariant json_var = p.parse(data, &ok);
    QJsonDocument json_var = QJsonDocument::fromJson(data, &ok);
    if (ok.error != QJsonParseError::NoError) {
        QLOG_INFO() << QString("error: ") << data;
        return;
    }
    QJsonObject json = json_var.object();
    if(JSON_REPLY_STATUSCODE_OK==json[JSON_KEY_STATUSCODE].toInt())
    {
        QLOG_INFO() << QString("login sucessful.[data->%1]").arg(QString(data));
        //emit sucessful(QString("当前门店[%1:%2]").arg(json[JSON_KEY_STOREID].toString()).arg(json[JSON_KEY_STORENAME].toString()));
        OBJREF(ReqDataManger).SetToken(json[JSON_KEY_TOKEN].toString());

        FmTakeaway_printer::Instance().setStoreName(json["store_name"].toString());
        QTimer::singleShot(json["sync_time"].toInt()*1000, this , SLOT(onPullOrders()));
        emit ChangeUiStatus("登录成功");
    }else
    {
        QString errorMsg = json[JSON_KEY_MSG].toString();
        QLOG_WARN() << QString("login failed.[msg->%1]").arg(errorMsg);
        m_lastError = QString("登录失败[%1]").arg(errorMsg);
        emit error(m_lastError);
    }
    return;
}

void BillsManger::onPullOrders()
{
    QByteArray pullOrdersObj = OBJREF(ReqDataManger).GetPullOrderData(m_timestamp);
    QLOG_TRACE() << QString("pull orders [data->%1].").arg(QString(pullOrdersObj));
    m_pullOrdersSocket.getNewJosn(Socket::Query, pullOrdersObj);
    return;
}

void BillsManger::onPullDeliversError(int socketError, const QString &message)
{
    QLOG_ERROR() << QString("pull delivers soket error [type:%1][msg:%2]").arg(socketError).arg(message);
    m_lastError = QString("网络连接出错,程序即将退出!");
    emit pullDeliversError(m_lastError, BillsManger::SOCKET);
}

void BillsManger::_ProceDeliversReply(const QByteArray &data)
{
    //QJson::Parser p;
    //bool ok = false;
    QJsonParseError ok;
    //QVariant json_var = p.parse(data, &ok);
    QJsonDocument json_var = QJsonDocument::fromJson(data, &ok);
    if (ok.error != QJsonParseError::NoError) {
        QLOG_INFO() << QString("error: ") << data;
        return;
    }
    QJsonObject json = json_var.object();

    if(JSON_REPLY_STATUSCODE_OK==json[JSON_KEY_STATUSCODE].toInt())
    {
        QLOG_INFO() << QString("pull delivers sucessful. [data->%1]").arg(QString(data));
        m_deliverObjs.clear();

        //QVariantList delivers = json[JSON_KEY_DELIVERS].toList();
        QJsonArray delivers = json[JSON_KEY_DELIVERS].toArray();
        foreach(QJsonValue deliver, delivers)
        {
            QJsonObject deliverObj = deliver.toObject();
            DeliverObject tmpDeliverObj;
            tmpDeliverObj.fetchDataFromJson(deliverObj);
            m_deliverObjs.append(tmpDeliverObj);
        }
        emit pullDeliversSucessful(QString("获取配送员成功!"));
    }else
    {
        QString errorMsg = json[JSON_KEY_MSG].toString();
        QLOG_WARN() << QString("pull delivers failed. msg[%1]").arg(errorMsg);
        m_lastError = QString("获取配送员失败[%1]").arg(errorMsg);
        emit pullDeliversError(m_lastError);
    }
    return;
}

void BillsManger::_ProcRefundReply(const QByteArray &data)
{
    //QJson::Parser p;
    //bool ok = false;
    QJsonParseError ok;
    //QVariant json_var = p.parse(data, &ok);
    QJsonDocument json_var = QJsonDocument::fromJson(data, &ok);
    if (ok.error != QJsonParseError::NoError) {
        QLOG_INFO() << QString("error: ") << data;
        return;
    }
    QJsonObject json = json_var.object();
    if(JSON_REPLY_STATUSCODE_OK==json[JSON_KEY_STATUSCODE].toInt()) {
        if (json[JSON_KEY_STATUS].toInt() == ORDERSTATUS_REFUND_ACCEPT) {
            QLOG_INFO() << QString("refund order accepted[%1] sucessful.").arg(json[JSON_KEY_ORDERID].toString());
            emit sucessful(OPERABTN_NAME_REFUND_ACCEPT + QString("成功"));
        }
        else if (json[JSON_KEY_STATUS].toInt() == ORDERSTATUS_REFUND_REJECT) {
            QLOG_INFO() << QString("refund order canceled[%1] sucessful.").arg(json[JSON_KEY_ORDERID].toString());
            emit sucessful(OPERABTN_NAME_REFUND_REJECT + QString("成功"));
        }

        int last = 0, current = 0;
        if (_OrderTableChanged(last, current, json)) {
            OrderObject *tmpOrderObj = m_ordersMap.value(json[JSON_KEY_ORDERID].toString());
            emit OrderTableChanged(last, current, tmpOrderObj);
        }
    }
    else {
        QString errorMsg = json[JSON_KEY_MSG].toString();
        QLOG_WARN() << QString("refund order[%1] failed.[msg->%2]").arg(json[JSON_KEY_ORDERID].toString())
                                                                   .arg(errorMsg);

        QString opt = "订单处理";

        m_lastError = QString("%1失败[%2]").arg(opt, errorMsg);
        emit error(m_lastError);
    }
    return;
}

void BillsManger::_ProcPullOrdersReply(const QByteArray &data)
{
    qDebug() << data;
    //QJson::Parser p;
    //bool ok = false;
    QJsonParseError ok;
    //QVariant json_var = p.parse(data, &ok);
    QJsonDocument json_var = QJsonDocument::fromJson(data, &ok);
    if (ok.error != QJsonParseError::NoError) {
        QLOG_INFO() << QString("error: ") << data;
        return;
    }
    QJsonObject json = json_var.object();
    if(JSON_REPLY_STATUSCODE_OK==json[JSON_KEY_STATUSCODE].toInt()) {
        QLOG_TRACE() << QString("pull orders sucessful.");

        QJsonArray orders = json[JSON_KEY_ORDERS].toArray();

        m_timestamp = json[JSON_KEY_TIMESTAMPS].toString();
        foreach (QJsonValue order, orders) {
           QJsonObject orderObj = order.toObject();
           OrderObject *tmpOrderObj = NULL;
           tmpOrderObj = m_ordersMap.value(orderObj[JSON_KEY_ORDERID].toString());

#          if FMTAKEOUT_DEBUG
           //! Simulation
           orderObj["delivery_status"] = 1;
           orderObj["channel"] = "bdwm";
           orderObj["channelName"] = "百度外卖";
#          endif

#          if FMTAKEOUT_RESERVATION
           orderObj["order_type"] = 2;
           qsrand(QDateTime::currentMSecsSinceEpoch());
           int salt = qrand() % 700;

           orderObj["delivery_time"] = orderObj["create_time"].toDouble() + 4680 + salt;
#          endif



           if(NULL==tmpOrderObj) {
               tmpOrderObj = new OrderObject(this);
               tmpOrderObj->fromJson(orderObj);
               qDebug() << "\r\n" << orderObj << "\r\n";

               m_ordersMap.insert(tmpOrderObj->order_id, tmpOrderObj);

               QString orderId= tmpOrderObj->order_id;
               if(!Database::getInstance().isExit(orderId))
               {
                   QString sql= QString("INSERT INTO %1 (paid_trans_id,fm_id,pay_ebcode) VALUES ('%2', '%3', '%4' );")
                           .arg(Database::getInstance().GetInfoTableName())
                           .arg(tmpOrderObj->order_id)
                           .arg(tmpOrderObj->fm_id).arg(tmpOrderObj->channel);
                   Database::getInstance().execSql(sql);

                   //写入商品信息
                   for(int i=0; i < tmpOrderObj->pvm.keys().count(); i++)
                   {
                       QString sqlProduct= QString("INSERT INTO %1 (fm_id,pid,consume_num,original_price) VALUES ('%2', '%3', %4, %5);")
                               .arg(Database::getInstance().GetProductTableName())
                               .arg(tmpOrderObj->fm_id).arg(tmpOrderObj->proList.at(i)->pid).arg(tmpOrderObj->proList.at(i)->productAmount)
                               .arg(tmpOrderObj->proList.at(i)->price);
                       Database::getInstance().execSql(sqlProduct);
                   }
               }

               if(tmpOrderObj->status == 2 && !Database::getInstance().isPrint(orderId))
               {
                   QLOG_INFO() << QString("[---print order---]")<<tmpOrderObj->toJson();
                   WBillControl::GetInstance().PrintLable(tmpOrderObj);
                   // 打印
//                   QLOG_INFO() << QString("[---begin print---]");
//                   if(FmTakeaway_printer::Instance().DoPrint(IniDataManger::Instance().GetPrinterName(), tmpOrderObj))
//                   {
//                       QLOG_INFO() << QString("[---print sucessful---]");
//                       Orderstatus::getInstance().printupdate(orderId,1);
//                   }else
//                   {
//                       QLOG_INFO() << QString("[---print failed---]");
//                   }
               }

               if(tmpOrderObj->status == 2 || tmpOrderObj->status ==4 )
               {
                   // 写入销售单
                   emit sWriteBill(tmpOrderObj);
               }
               if(tmpOrderObj->status == 3)
               {
                   // 撤销销售单
                   emit sRefundBill(tmpOrderObj->fm_id);
               }

               QDateTime orderTime =  QDateTime::fromTime_t(tmpOrderObj->create_time);
               if(orderTime<m_dateBusiness) {
                   QLOG_INFO() << QString("overdue order[%1->%2].[date->%3]").arg(tmpOrderObj->order_id)
                                  .arg(GetOrderStatusName(tmpOrderObj->status, tmpOrderObj->delivery_status)).arg(orderTime.toString("yyyy-MM-dd"));
               }
               else {
                   QLOG_INFO() << QString("new order[%1->%2].[data->%3]").arg(tmpOrderObj->order_id)
                        .arg(GetOrderStatusName(tmpOrderObj->status, tmpOrderObj->delivery_status)).arg(_GetJsonString(orderObj));

                   int last = 0, current = 0;
                   _OrderTableChanged(last, current, orderObj);
                   if (current != TABLE_VID_NO_DRIVER){
                       OrderObject *tmpOrderObj = m_ordersMap.value(orderObj[JSON_KEY_ORDERID].toString());
                       emit OrderTableChanged(last, current, tmpOrderObj);
                   }
               }
           } // if (NULL == tmpOrderObj)
           else {
               if(orderObj[JSON_KEY_STATUS].toInt()==tmpOrderObj->status
                       && orderObj[JSON_KEY_DELIVERSTATUS].toInt() == tmpOrderObj->delivery_status) {
                   QLOG_TRACE() << QString("already exists order.[data->%1]").arg(tmpOrderObj->order_id);
               }
               else {
                   QLOG_INFO() << QString("old order[%1->%2].[data->%3]").arg(tmpOrderObj->order_id)
                        .arg(GetOrderStatusName(orderObj[JSON_KEY_STATUS].toInt(), orderObj[JSON_KEY_DELIVERSTATUS].toInt())).arg(_GetJsonString(orderObj));
                   int last = 0, current = 0;
                   if (_OrderTableChanged(last, current, orderObj)) {
                       OrderObject *tmpOrderObj = m_ordersMap.value(orderObj[JSON_KEY_ORDERID].toString());
                       emit OrderTableChanged(last, current, tmpOrderObj);
                   }
               }

//               if(tmpOrderObj->status == 2 || tmpOrderObj->status ==4 )
//               {
//                   // 写入销售单 对于已经写入pos的销售单，不用再次写入
//                   //emit sWriteBill(tmpOrderObj);
//               }
               if(tmpOrderObj->status == 3)
               {
                   // 撤销销售单
                   emit sRefundBill(tmpOrderObj->fm_id);
               }
           }
//           if(m_timestamp.toLongLong()<tmpOrderObj->timestamp.toLongLong())
//           {
//               m_timestamp = tmpOrderObj->timestamp;
//               QLOG_INFO() << QString("update timestamp.[data->%1]").arg(m_timestamp);
//           }
        }

        // 0表示还有未拉取的数据,单次拉取上限为10单
        if(0==json[JSON_KEY_SYNCTIME].toInt()) {
            onPullOrders();
        }
        //! No newer orders
        else {
            //! Check reservations
            _CheckReservations();

            // 设置下次拉取订单的计时器
            QLOG_TRACE() << QString("set pull orders timer.intercal[%1s]").arg(json[JSON_KEY_SYNCTIME].toInt());
            m_pullOrdersTimer.start(json[JSON_KEY_SYNCTIME].toInt()*1000);
        }

    }
    else {
        QString errorMsg = json[JSON_KEY_MSG].toString();
        QLOG_ERROR() << QString("pull orders failed.[msg->%1]").arg(errorMsg);
        m_lastError = QString("获取订单失败[%1]").arg(errorMsg);
        emit pullOrdersError(m_lastError);
    }
    return;
}

void BillsManger::ProcOrders(const QString &operaName, const QString &orderId, int reasonCode,  const DeliverObject& deliverObj)
{
    QLOG_INFO() << "[" << orderId << "]operation: " << operaName << ", " << reasonCode;
    if(!operaName.compare(OPERABTN_NAME_CONFIRM))
    {
        OrderObject *order = 0;
        if (m_ordersMap.contains(orderId)) {
            order = m_ordersMap[orderId];
        }
        if (order && order->has_mate && !order->mate_assigned) {
            QLOG_INFO() << "mates assignment needed.";
            m_lastError = "请先指定加料!";
            emit error(QString(OPERABTN_NAME_CONFIRM) + "失败");
            return;
        }
        else if (order && order->has_mate && order->mate_assigned){
             WriteBill(orderId);
        }
       QByteArray confirmObj = OBJREF(ReqDataManger).GetConfirmOrderData(orderId, deliverObj.id, deliverObj.name, deliverObj.phone);
       QLOG_INFO() << QString("confirm order [data->%1]").arg(QString(confirmObj));
       m_procOrdersSocket.getNewJosn(Socket::Accpet, confirmObj);
    }else if(!operaName.compare(OPERABTN_NAME_SEND) || !operaName.compare(OPERABTN_NAME_SELFSEND))
    {
        QByteArray sendObj = OBJREF(ReqDataManger).GetSendOrderData(orderId);
        QLOG_INFO() << QString("sendout order [data->%1]").arg(QString(sendObj));
        m_procOrdersSocket.getNewJosn(Socket::Sendout, sendObj);
    }else if(!operaName.compare(OPERABTN_NAME_COMPLETE))
    {
        QByteArray completeObj = OBJREF(ReqDataManger).GetOrderFinshData(orderId);
        QLOG_INFO() << QString("complete order [data->%1]").arg(QString(completeObj));
        m_procOrdersSocket.getNewJosn(Socket::Finsh, completeObj);
    }else if(!operaName.compare(OPERABTN_NAME_REJECT))
    {
        QByteArray rejectObj = OBJREF(ReqDataManger).GetRefuseOrderData(reasonCode, orderId);
        QLOG_INFO() << QString("reject order [data->%1]").arg(QString(rejectObj));
        m_procOrdersSocket.getNewJosn(Socket::Reject, rejectObj);
    }
    else if (!operaName.compare(OPERABTN_NAME_REFUND_ACCEPT)) {
        QByteArray refundObj = OBJREF(ReqDataManger).GetRefundOrderData(orderId, QString("同意退款"));
        QLOG_INFO() << QString("refund order accepted [data->%1]").arg(QString(refundObj));
        m_procOrdersSocket.getNewJosn(Socket::AgreeRefund, refundObj);
    }
    else if (!operaName.compare(OPERABTN_NAME_REFUND_REJECT)) {
        QByteArray refundObj = OBJREF(ReqDataManger).GetNoRefundOrderData(orderId, QString("拒绝退款"));
        QLOG_INFO() << QString("refund order canceled [data->%1]").arg(QString(refundObj));
        m_procOrdersSocket.getNewJosn(Socket::CancelRefund, refundObj);
    }
    else if (!operaName.compare(OPERABTN_NAME_MAKE)) {
        bool res = WriteBill(orderId);
        QLOG_INFO() << QString("manually making order[%1] sucessful.").arg(orderId);
        _CheckReservations();
//        emit sucessful(OPERABTN_NAME_MAKE + QString("成功"));
        if (res) {
            emit sucessful(OPERABTN_NAME_MAKE + QString("成功"));
        }
        else {
            emit error(OPERABTN_NAME_MAKE + QString("失败"));
        }
    }
    else if (!operaName.compare(OPERABTN_NAME_CONFIRM_MATE)) {
        bool res = WriteBill(orderId);
        QLOG_INFO() << QString("confirm mates of order[%1] %2.").arg(orderId, (res ? "successful" : "failed"));
        if (res) {
            OrderObject *order = 0;
            if (m_ordersMap.contains(orderId)) {
                order = m_ordersMap[orderId];
                int last = TABLE_VID_CONFIRM_MATE;
                int current = GetTableVID(order);
                emit OrderTableChanged(last, current, order);
            }

            emit sucessful(OPERABTN_NAME_CONFIRM_MATE + QString("成功"));
        }
        else {
            emit error(OPERABTN_NAME_CONFIRM_MATE + QString("失败"));
        }
    }
    else {
        QLOG_WARN() << "unknown operation: " << operaName;
    }
    return;
}

void BillsManger::ProcOrders(const QString &operaName, const QString &orderId, const QString &desc)
{
    QLOG_INFO() << "[" << orderId << "]operation: " << operaName << ", " << desc;
    if (!operaName.compare(OPERABTN_NAME_REFUND_ACCEPT)) {
        QByteArray refundObj = OBJREF(ReqDataManger).GetRefundOrderData(orderId, desc);
        QLOG_INFO() << QString("refund order accepted [data->%1]").arg(QString(refundObj));
        m_procOrdersSocket.getNewJosn(Socket::AgreeRefund, refundObj);
    }
    else if (!operaName.compare(OPERABTN_NAME_REFUND_REJECT)) {
        QByteArray refundObj = OBJREF(ReqDataManger).GetNoRefundOrderData(orderId, desc);
        QLOG_INFO() << QString("refund order canceled [data->%1]").arg(QString(refundObj));
        m_procOrdersSocket.getNewJosn(Socket::CancelRefund, refundObj);
    }
    else {
        QLOG_WARN() << "unknown operation: " << operaName;
    }
    return;
}

void BillsManger::StopPullOrder()
{
    m_pullOrdersTimer.stop();
    return;
}

void BillsManger::PullDelivers()
{
    QByteArray pullDevObj = OBJREF(ReqDataManger).GetDeliverData(FmTakeaway_db::Instance().GetBranchNum());
    QLOG_INFO() << QString("pull delivers [data->%1].").arg(QString(pullDevObj));
    m_pullDeliversSocket.getNewJosn(Socket::Delivers, pullDevObj);
    return;
}

QList<DeliverObject> BillsManger::GetDelivers()
{
    return m_deliverObjs;
}

bool BillsManger::WriteBill(OrderObject *order)
{
    QLOG_INFO() << "writting bill.";

    QString order_file = OBJREF(IniDataManger).GetBillFile();
    QString vivi_file = OBJREF(IniDataManger).GetViviBillFile();

    if (order) {
        if (order->has_mate && !order->mate_assigned) {
            QLOG_INFO() << "mates assignment needed." << order->loneMateList;
            m_lastError = QString("请先指定加料!");
            return false;
        }

        if (order->billed && QFile(order->bill_file).exists()) {
            QLOG_INFO() << "bill file " << order->bill_file << " exists";
            return true;
        }

        QLOG_INFO() << QString("writting bill for order [%1]").arg(order->order_id);
        order_file += ("." + order->channel + "."
                + QDateTime::fromTime_t(order->create_time).toString("yyyyMMddhhmmss") + "."
                + order->order_index);
        QFile file(order_file);
        QFileInfo file_info(order_file);
        vivi_file += file_info.fileName();
        QFileInfo vivi_file_info(vivi_file);
        if (!vivi_file_info.exists() && file.open(QFile::WriteOnly)) {
            QJsonObject fv, fi, fp;
            fi["pay_ebcode"] = order->channel;
            fi["pay_ebcode_str"] = order->channelName;
            fi["total_amount"] = order->total_fee;

            int pos_amount = 0, rider_amount = 0;

            bool is_bdwm = (order->channel == CHANNEL_ID_BD);
            if (is_bdwm) {
                pos_amount = order->shop_fee;
                //! ServiceFee = TotalFee - SendFee - ShopFee - ShopDiscountFee
                fi["incentives_merchant"] = order->service_fee + order->dis_shop_fee;
                rider_amount = pos_amount;
            }
            else {
                pos_amount = order->shop_fee - order->dis_shop_fee;
                fi["incentives_merchant"] = order->dis_shop_fee;
            }

            if (order->delivery_type == SHIPTYPE_SELF) {
                pos_amount += order->send_fee;
            }

            fi["incentives_forum"] = order->discount_fee - order->dis_shop_fee;
            fi["pos_amount"] = pos_amount;
            fi["rider_pay"] = rider_amount;
            fi["rider_no"] = order->order_index;
            fi["paid_trans_id"] = order->order_id;
            fi["fm_id"] = order->fm_id;

            fv["fm_cmd"] = "put_order";
            fv["fm_ver"] = "1.0";
            fv["pay_id"] = fi;

            fp["name"] = order->customer;
            fp["phone"] = order->phone;
            fp["address"] = order->address;
            fp["memo"] = order->remark;
            fv["customer"] = fp;

            QJsonArray pds;   //! Products
            QList<ComdObject*>  product_list = order->pvm.keys();
            foreach(ComdObject*product, product_list) {
                QJsonObject vm;
                vm["consume_num"] = product->productAmount;
                vm["pid"] = product->pid;
                vm["product_name"] = product->name;
                vm["original_price"] = product->price;

                QVariant props_var = product->property_tags;
                QString str_cond;
                QList<ComdObject*> pmlist = order->pvm[product];
                if (!pmlist.isEmpty()) {
                    int end_pos = pmlist.size() - 1;
                    foreach(ComdObject *mo, pmlist) {
                        //CHANGE
                        //str_cond += QString("%1|%2|%3|null").arg(mo->name, UnitConver(mo->price), QString::number(mo->productAmount));
                        str_cond += QString("%1|%2|%3|null").arg(mo->pid, UnitConver(mo->price), QString::number(mo->productAmount));
                        if (end_pos != pmlist.indexOf(mo)) {
                            str_cond += ",";
                        }
                    }
                }

                if (!props_var.isNull()) {
                    if (!str_cond.isEmpty()) str_cond += ",";
                    QString props = props_var.toString();
                    QStringList props_list = props.split("|");
                    int end_pos = props_list.size() - 1;
                    if(product_list.indexOf(product) == 0 && order->delivery_type == SHIPTYPE_SELF) {
                        str_cond += QString("配送费|%1|1|null").arg(UnitConver(order->send_fee));
                        if (props_list.size() > 0) {
                            str_cond += ",";
                        }
                    }
                    foreach(QString prop, props_list) {
                        if (props_list.indexOf(prop) == end_pos) {
                            str_cond += prop + "|0.0|1|null";
                        }
                        else {
                            str_cond += prop + "|0.0|1|null,";
                        }
                    }
                }
                else {
                    if(product_list.indexOf(product) == 0 && order->delivery_type == SHIPTYPE_SELF) {
                        str_cond += QString("配送费|%1|1|null").arg(UnitConver(order->send_fee));
                    }
                }

                vm["condiments"] = str_cond;

                pds << vm;
            }

            //! Mates that are took as products
            foreach(ComdObject*product, order->loneMateList) {
                QJsonObject vm;
                vm["consume_num"] = 1;
                vm["pid"] = product->pid;
                vm["product_name"] = product->name;
                vm["original_price"] = product->price;
                vm["condiments"] = "";

                for(int i = 0; i < product->productAmount; ++i) {
                    pds << vm;
                }
            }

            fv["products"] = pds;

           // QJson::Serializer s;
            bool ok = true;
            QByteArray d = QJsonDocument(fv).toJson(QJsonDocument::Compact);
            if (ok) {
                QLOG_INFO() << "record order ok:";
                file.write(d);
                file.close();
                order->billed = true;
                order->bill_file = file.fileName();
                return true;
            }
            else {
                QLOG_ERROR() << "failed serialize data: " << fv;
            }
        }
        else if (vivi_file_info.exists()) {
            QLOG_ERROR() << "already processed order file: " << vivi_file_info.fileName();
        }
        else {
            QLOG_ERROR() << "failed opening bill file:" << order_file;
        }
    }
    else {
        QLOG_ERROR() << "order object is null.";
    }

    return false;
}

bool BillsManger::WriteBill(const QString &oid)
{
    if (m_ordersMap.contains(oid)) {
        OrderObject *order = m_ordersMap[oid];
        return WriteBill(order);
    }

    return false;
}

bool BillsManger::WriteTill(const OrderObject *order)
{
    QString till_file = OBJREF(IniDataManger).GetTillFile();

    if (order) {
        QFile file(till_file);
        if (file.open(QFile::WriteOnly)) {
            QJsonObject fv;
            fv["fm_ver"] = "1.0";

            int pos_amount = order->shop_fee;
            if (order->delivery_type == 0) {
                pos_amount += order->send_fee;
            }

            fv["money"] = pos_amount - order->dis_shop_fee;

            //QJson::Serializer s;
            bool ok = true;
            QByteArray d = QJsonDocument(fv).toJson(QJsonDocument::Compact);
            if (ok) {
                QLOG_INFO() << "record till data ok:" << fv;
                file.write(d);
                file.close();
            }
            else {
                QLOG_ERROR() << "Failed serialize data: " << fv;
            }
        }
        else {
            QLOG_ERROR() << "failed opening till file: " << till_file;
        }
    }
    else {
        QLOG_ERROR() << "order object is null.";
    }

    return false;
}

int BillsManger::GetTableVID(OrderObject *order)
{
    return _GetTableVID(order);
}

int BillsManger::GetTableVID(const QJsonObject &order)
{
    if (order.contains(JSON_KEY_PRODUCTS)) {
        int order_status = order[JSON_KEY_STATUS].toInt();
        int delivery_status = order[JSON_KEY_DELIVERSTATUS].toInt();
        int delivery_type = order[JSON_KEY_DELIVERYTYPE].toInt();
#if FMTAKEOUT_NODT
        delivery_type = FMTAKEOUT_TYPE;
#endif
        OrderObject *order_obj = m_ordersMap.value(order[JSON_KEY_ORDERID].toString());
        order_obj->status = order_status;
        order_obj->delivery_status = delivery_status;
        order_obj->delivery_type = delivery_type;
        return _GetTableVID(order_obj);
    }
    else {
        OrderObject *order_obj = m_ordersMap.value(order[JSON_KEY_ORDERID].toString());
        order_obj->status = order[JSON_KEY_STATUS].toInt();
        return GetTableVID(order_obj);
    }

    return TABLE_VID_UNKNOWN;
}

QString BillsManger::LastError() const
{
    return m_lastError;
}

void BillsManger::onOperatorChanged(const FmTakeaway_db::OperatorInfo &opera)
{
    m_operator = opera;
}

void BillsManger::onGetNewStoreInfo(const QJsonObject &json)
{
    if(m_pullOrdersTimer.isActive())
        m_pullOrdersTimer.stop();
    m_timestamp=QString("");
    m_ordersMap.clear();
    m_rsv_orders.clear();   //! Reservation orders' ID
    m_deliverObjs.clear();
    emit CLearTable();

    IniDataManger::Instance().SetStoreJson(json);
    Login();
}

void BillsManger::onRecvReply(const Socket::RequstType type, const QByteArray &json)
{
    m_retry_cnt = 0;
    switch(type)
    {
        case Socket::Login:
            _ProcLoginReply(json);
            break;
        case Socket::SetPosNo:
            break;
        case Socket::Query:
            _ProcPullOrdersReply(json);
            break;
        case Socket::Accpet:
            _ProcConfirmReply(json);
            break;
        case Socket::Reject:
            _ProcRejectReply(json);
            break;
        case Socket::Bind:
            break;
        case Socket::Consume:
            break;
        case Socket::Sendout:
            _ProceSendoutReply(json);
            break;
        case Socket::UpdStock:
            break;
        case Socket::Finsh:
            _ProceCompleteReply(json);
            break;
        case Socket::AgreeRefund:
        case Socket::CancelRefund:
            _ProcRefundReply(json);
            break;
        case Socket::Delivers:
            _ProceDeliversReply(json);
            break;
        default:
            break;
    }
    return;
}

void BillsManger::_ProcConfirmReply(const QByteArray &data)
{
//    QJson::Parser p;
//    bool ok = false;
    QJsonParseError ok;
    QJsonDocument json_var = QJsonDocument::fromJson(data, &ok);
    if (ok.error != QJsonParseError::NoError) {
        QLOG_INFO() << QString("error: ") << data;
        return;
    }
    QJsonObject json = json_var.object();
    if(JSON_REPLY_STATUSCODE_OK==json[JSON_KEY_STATUSCODE].toInt())
    {
        QLOG_INFO() << QString("confrim order[%1] sucessful.").arg(json[JSON_KEY_ORDERID].toString());
        OrderObject *tmpOrderObj = m_ordersMap.value(json[JSON_KEY_ORDERID].toString());
        emit sucessful( OPERABTN_NAME_CONFIRM + QString("成功!"));
        //!
        int last = 0, current = 0;
        if (_OrderTableChanged(last, current, json)) {
            emit OrderTableChanged(last, current, tmpOrderObj);
        }
//        FmTakeaway_printer::Instance().DoPrint(OBJREF(IniDataManger).GetPrinterName(), m_ordersMap.value(json[JSON_KEY_ORDERID].toString()));
        OrderObject *orderObj  = m_ordersMap.value(json[JSON_KEY_ORDERID].toString());
        QString filePath = QString("%1/orders/%2_%3.%4.%5.%6").arg(QApplication::applicationDirPath())
                .arg(orderObj->order_id).arg(m_operator.id)
                .arg(m_operator.name).arg(m_operator.shiftId).arg(m_operator.shiftName);
        QFile file(filePath);
        file.open(QIODevice::WriteOnly);
        file.write(orderObj->toString().data());
        file.close();
        OBJREF(BillsManger).WriteBill(orderObj);
    }else
    {
        QString errorMsg = json[JSON_KEY_MSG].toString();
        QLOG_WARN() << QString("confrim order[%1] failed.[msg->%2]").arg(json[JSON_KEY_ORDERID].toString())
                                                                    .arg(errorMsg);
        m_lastError = OPERABTN_NAME_CONFIRM + QString("失败[%1]").arg(errorMsg);
        emit error(m_lastError);
    }
    return;
}

void BillsManger::_ProcRejectReply(const QByteArray &data)
{
    QJsonParseError ok;
    QJsonDocument json_var = QJsonDocument::fromJson(data, &ok);
    if (ok.error != QJsonParseError::NoError) {
        QLOG_INFO() << QString("error: ") << data;
        return;
    }
    QJsonObject json = json_var.object();
    if(JSON_REPLY_STATUSCODE_OK==json[JSON_KEY_STATUSCODE].toInt())
    {
        QLOG_INFO() << QString("reject order[%1] sucessful.").arg(json[JSON_KEY_ORDERID].toString());
        OrderObject *tmpOrderObj = m_ordersMap.value(json[JSON_KEY_ORDERID].toString());
        if (tmpOrderObj->channel != CHANNEL_ID_ELE) {
            QString msg = DETAILTIP_AUTO_CANCEL;
            msg = msg.arg(tmpOrderObj->order_id);
            emit sucessful(OPERABTN_NAME_REJECT + QString("成功!"), msg);
        }
        else {
            emit sucessful(OPERABTN_NAME_REJECT + QString("成功!"));
        }
        //!
        int last = 0, current = 0;
        if (_OrderTableChanged(last, current, json)) {
            OrderObject *tmpOrderObj = m_ordersMap.value(json[JSON_KEY_ORDERID].toString());
            emit OrderTableChanged(last, current, tmpOrderObj);
        }
    }else
    {
        QString errorMsg = json[JSON_KEY_MSG].toString();
        QLOG_WARN() << QString("reject order[%1] failed.[msg->%2]").arg(json[JSON_KEY_ORDERID].toString())
                                                                    .arg(errorMsg);
        m_lastError = OPERABTN_NAME_REJECT + QString("失败[%1]").arg(errorMsg);
        emit error(m_lastError);
    }
    return;
}

void BillsManger::_ProceSendoutReply(const QByteArray &data)
{
//    QJson::Parser p;
//    bool ok = false;
//    QVariant json_var = p.parse(data, &ok);
//    if (!ok) {
//        QLOG_INFO() << QString("error: ") << data;
//        return;
//    }
//    QVariantMap json = json_var.toMap();
    QJsonParseError ok;
    QJsonDocument json_var = QJsonDocument::fromJson(data, &ok);
    if (ok.error != QJsonParseError::NoError) {
        QLOG_INFO() << QString("error: ") << data;
        return;
    }
    QJsonObject json = json_var.object();
    if(JSON_REPLY_STATUSCODE_OK==json[JSON_KEY_STATUSCODE].toInt())
    {
        QLOG_INFO() << QString("sendout order[%1] sucessful.").arg(json[JSON_KEY_ORDERID].toString());
        //!
        int last = 0, current = 0;
        if (_OrderTableChanged(last, current, json)) {
            OrderObject *tmpOrderObj = m_ordersMap.value(json[JSON_KEY_ORDERID].toString());
            emit OrderTableChanged(last, current, tmpOrderObj);
        }

        OrderObject *tmpOrderObj = m_ordersMap.value(json[JSON_KEY_ORDERID].toString());
        //
        if (tmpOrderObj->channel == CHANNEL_ID_BD) {
            QString msg = DETAILTIP_BD_SEND;
            msg = msg.arg(tmpOrderObj->order_id, UnitConver(tmpOrderObj->shop_fee));
            emit sucessful(QString("骑士取餐成功!"), msg);
            OBJREF(BillsManger).WriteTill(tmpOrderObj);
        }
        else {
            if (tmpOrderObj->delivery_type == 1) {
                emit sucessful(OPERABTN_NAME_SEND + QString("成功!"));
            }
            else {
                emit sucessful(OPERABTN_NAME_SELFSEND + QString("成功!"));
            }
        }
    }else {
        QString errorMsg = json[JSON_KEY_MSG].toString();
        QLOG_WARN() << QString("sendout order[%1] failed.[msg->%2]").arg(json[JSON_KEY_ORDERID].toString())
                                                                    .arg(errorMsg);
        m_lastError = QString("骑士取餐失败[%1]").arg(errorMsg);
        emit error(m_lastError);
    }
    return;
}

void BillsManger::_ProceCompleteReply(const QByteArray &data)
{
//    QJson::Parser p;
//    bool ok = false;
//    QVariant json_var = p.parse(data, &ok);
//    if (!ok) {
//        QLOG_INFO() << QString("error: ") << data;
//        return;
//    }
//    QVariantMap json = json_var.toMap();
    QJsonParseError ok;
    QJsonDocument json_var = QJsonDocument::fromJson(data, &ok);
    if (ok.error != QJsonParseError::NoError) {
        QLOG_INFO() << QString("error: ") << data;
        return;
    }
    QJsonObject json = json_var.object();
    if(JSON_REPLY_STATUSCODE_OK==json[JSON_KEY_STATUSCODE].toInt()) {
        QLOG_INFO() << QString("complete order[%1] sucessful.[%2].").arg(json[JSON_KEY_ORDERID].toString()).arg(json[JSON_KEY_STATUS].toString());
        //!
        int last = 0, current = 0;
        if (_OrderTableChanged(last, current, json)) {
            OrderObject *tmpOrderObj = m_ordersMap.value(json[JSON_KEY_ORDERID].toString());
            emit OrderTableChanged(last, current, tmpOrderObj);
        }
        emit sucessful(QString("完成成功!"));

    }
    else {
        QString errorMsg = json[JSON_KEY_MSG].toString();
        QLOG_WARN() << QString("complete order[%1] failed.[msg->%2]").arg(json[JSON_KEY_ORDERID].toString())
                                                                    .arg(errorMsg);
        m_lastError = QString("完成失败[%1]").arg(errorMsg);
        emit error(m_lastError);
    }
    return;
}


void BillsManger::onError(int socketError, const QString &message)
{
    QLOG_ERROR() << QString("soket error [type:%1][msg:%2]").arg(socketError).arg(message);
    m_lastError = QString("网络连接出错,程序即将退出!");
    emit error(m_lastError, BillsManger::SOCKET);
}

void BillsManger::onPullOrdersError(int socketError, const QString &message)
{
    ++m_retry_cnt;
    QLOG_ERROR() << QString("pull orders soket error [type:%1][msg:%2]").arg(socketError).arg(message);
    if (m_retry_cnt >= m_retry_max) {
        //! 2nd or more times that encountered pull order error, without showing error msg box again
        if (m_retry_cnt - 1 >= m_retry_max) {
            return;
        }
        m_lastError = QString("网络连接出错,程序即将退出!");
        emit pullOrdersError(m_lastError, BillsManger::SOCKET);
    }
}

OrderObject *BillsManger::GetOrderObjByOrderId(const QString &orderId)
{
    return m_ordersMap.value(orderId);
}

BillsManger::BillsManger()
    :m_timestamp("0"),
      m_retry_cnt(0),
      m_retry_max(0)
{
    IniDataManger::FmServerInfo fmsvInfo;
    fmsvInfo = OBJREF(IniDataManger).GetFmServerInfo();
    QLOG_INFO() << QString("fmserver [be use proxy->%1].").arg(fmsvInfo.bProxy);

    if(fmsvInfo.bProxy) {
        QLOG_INFO() << QString("fmserver [host->%1] [port->%2].").arg(fmsvInfo.host).arg(fmsvInfo.port);
        m_loginSocket.setByTcp(fmsvInfo.host, fmsvInfo.port);
        m_pullOrdersSocket.setByTcp(fmsvInfo.host, fmsvInfo.port);
        m_procOrdersSocket.setByTcp(fmsvInfo.host, fmsvInfo.port);
        m_pullDeliversSocket.setByTcp(fmsvInfo.host, fmsvInfo.port);
    }
    else {
        QLOG_INFO() << QString("fmserver [url->%1].").arg(fmsvInfo.url);
        m_loginSocket.setByHttp(fmsvInfo.url);
        m_pullOrdersSocket.setByHttp(fmsvInfo.url);
        m_procOrdersSocket.setByHttp(fmsvInfo.url);
        m_pullDeliversSocket.setByHttp(fmsvInfo.url);
    }

    // 获取当前营业日
    QJsonObject dd = OBJREF(IniDataManger).GetStoreJson();
    QJsonObject store = dd["store_info"].toObject();

    m_dateBusiness = QDateTime::fromString(store["business_date"].toString(), "yyyyMMdd");
    m_dateBusiness.setTime(QTime(0,0));
    QLOG_INFO() << QString("current dbusiness.[%1]").arg(m_dateBusiness.toString("yyyy-MM-dd"));

    m_retry_max = OBJREF(IniDataManger).GetMaxRetry();
    connect(&m_loginSocket, SIGNAL(recvReturn(Socket::RequstType,QByteArray)), this, SLOT(onRecvReply(Socket::RequstType,QByteArray)));
    connect(&m_pullOrdersSocket, SIGNAL(recvReturn(Socket::RequstType,QByteArray)), this, SLOT(onRecvReply(Socket::RequstType,QByteArray)));
    connect(&m_procOrdersSocket, SIGNAL(recvReturn(Socket::RequstType,QByteArray)), this, SLOT(onRecvReply(Socket::RequstType,QByteArray)));
    connect(&m_pullDeliversSocket, SIGNAL(recvReturn(Socket::RequstType,QByteArray)), this, SLOT(onRecvReply(Socket::RequstType,QByteArray)));
    connect(&m_loginSocket, SIGNAL(error(int,QString)), this, SLOT(onError(int,QString)));
    connect(&m_procOrdersSocket, SIGNAL(error(int,QString)), this, SLOT(onError(int,QString)));
    connect(&m_pullOrdersSocket, SIGNAL(error(int,QString)), this, SLOT(onPullOrdersError(int,QString)));
    connect(&m_pullDeliversSocket, SIGNAL(error(int,QString)), this, SLOT(onPullDeliversError(int,QString)));
    connect(&m_pullOrdersTimer, SIGNAL(timeout()), this, SLOT(onPullOrders()));

    connect(&WBillControl::GetInstance(), SIGNAL(sUpdatePosRes(const OrderObject*)), this, SLOT(onUpdateWritePosStatus(const OrderObject*)));

    connect(this, SIGNAL(sWriteBill(const OrderObject*)), &WBillControl::GetInstance(), SLOT(DoOrderEntry(const OrderObject*)));
    connect(this, SIGNAL(sRefundBill(const QString&)), &WBillControl::GetInstance(), SLOT(RefundOrder(const QString&)));
}

QString BillsManger::_GetJsonString(const QJsonObject &obj)
{
//    QJson::Serializer serializer;
//    return serializer.serialize(obj);

    return QString(QJsonDocument(obj).toJson(QJsonDocument::Compact).replace("\"", ""));
}

bool BillsManger::_OrderTableChanged(int &last, int &current, const QJsonObject &new_order)
{
    bool changed = false;
    bool is_full_order = new_order.contains(JSON_KEY_PRODUCTS);
    OrderObject *old_order = m_ordersMap.value(new_order[JSON_KEY_ORDERID].toString());

    last = GetTableVID(old_order);
    current = GetTableVID(new_order);

    if (current == TABLE_VID_RESERVATION && !m_rsv_orders.contains(old_order->order_id)) {
        m_rsv_orders << old_order->order_id;
    }

    //! Assign new value;
    if (last != current) {
        if (is_full_order) {
            old_order->fromJson(new_order);
        }
        else {
            old_order->status = new_order[JSON_KEY_STATUS].toInt();
            if (new_order.contains(JSON_KEY_DELIVERYTYPE)) {
                old_order->delivery_type = new_order[JSON_KEY_DELIVERYTYPE].toInt();
            }
            if (new_order.contains(JSON_KEY_DELIVERSTATUS)) {
                old_order->delivery_status = new_order[JSON_KEY_DELIVERSTATUS].toInt();
            }
        }
        changed = true;
    }

    switch(current) {
    case TABLE_VID_SHIPSELF:
    case TABLE_VID_MAKING:
    case TABLE_VID_SELF_MAKING:
    case TABLE_VID_CONFIRM_MATE:
    case TABLE_VID_SHIPING:
    case TABLE_VID_SELF_SHIPING:
    case TABLE_VID_FINISH:
    case TABLE_VID_SELF_FINISH:
    case TABLE_VID_REFUND:
    case TABLE_VID_SELF_REFUND:
    case TABLE_VID_PENDING:
    case TABLE_VID_SELF_PENDING:
        QLOG_INFO() << QString("order %1 status ").arg(old_order->order_id) + (changed ? "changed" : "not changed.");
        break;
    case TABLE_VID_NO_DRIVER:
        QLOG_INFO() << QString("ignore order %1 until driver assigned.").arg(old_order->order_id);
        changed = false;
        break;
    default:
        QLOG_INFO() << "unknown order status =>\n" << new_order;
        break;
    }

    return changed;
}

int BillsManger::_GetTableVID(OrderObject *order)
{
    int dt = order->delivery_type;
    int ds = order->delivery_status;
    int os = order->status;
    int bd = order->billed;
    int elapse = 0;

    int table_id = TABLE_VID_UNKNOWN;
#if FMTAKEOUT_NODT
    dt = FMTAKEOUT_TYPE;
#endif

    bool platship = (dt == SHIPTYPE_PLATFORM);
    bool reservation = false;

    quint32 ct = QDateTime::currentMSecsSinceEpoch() / 1000;

    if (!bd && order->delivery_time != 0) {
        elapse = (int)order->delivery_time - int(ct);
        QLOG_INFO() << "reservation time left: " << elapse;
        if (elapse > 3600) {
            reservation = true;
        }
    }
    else if (bd) {
        QLOG_INFO() << "previously processed order: " << order->order_id;
    }

    switch(os) {
    case ORDERSTATUS_CONFIRMED:
        switch(ds) {
        case SHIPSTATUS_CONFIRMED: table_id = (reservation ? TABLE_VID_RESERVATION : (platship ? TABLE_VID_MAKING : TABLE_VID_SELF_MAKING)); break;
        case SHIPSTATUS_CANCELED: table_id = (reservation? TABLE_VID_RESERVATION : TABLE_VID_SHIPSELF); break;
        case SHIPSTATUS_PENDING: table_id = TABLE_VID_NO_DRIVER; break;
        default: break;
        }

        break;
    case ORDERSTATUS_SHIPPING: table_id = (platship ? TABLE_VID_SHIPING : TABLE_VID_SELF_SHIPING); break;
    case ORDERSTATUS_ARRIVED:
    case ORDERSTATUS_COMPLETE: table_id = (platship ? TABLE_VID_FINISH : TABLE_VID_SELF_FINISH); break;
    case ORDERSTATUS_REFUND: table_id = (platship ? TABLE_VID_REFUND : TABLE_VID_SELF_REFUND); break;
    default: table_id = (platship ? TABLE_VID_PENDING : TABLE_VID_SELF_PENDING); break;
    }

    //! If mates have't been assigned, keep orders stay in current table
    switch(table_id) {
    case TABLE_VID_MAKING:
    case TABLE_VID_SELF_MAKING:
    case TABLE_VID_SHIPING:
    case TABLE_VID_SELF_SHIPING:
    case TABLE_VID_FINISH:
    case TABLE_VID_SELF_FINISH:
        if (!order->billed && order->has_mate) {
            table_id = TABLE_VID_CONFIRM_MATE;
        }
        break;
    default:
        break;
    }

    QLOG_INFO() << QString("os: %1, ds: %2, dt: %3 elapse: %4 => table: %5").arg(QString::number(os),
                                                                                 QString::number(ds),
                                                                                 QString::number(dt),
                                                                                 QString::number(elapse),
                                                                                 QString::number(table_id));
    return table_id;
}

int BillsManger::_CalcPosAmount(const QByteArray &order)
{
    return 0;
}

void BillsManger::_CheckReservations()
{
    QLOG_INFO() << "checking reservations " << m_rsv_orders;
    QStringList tmp_rsvs = m_rsv_orders;
    foreach(QString rsv_order, tmp_rsvs) {
        OrderObject *orderObj = m_ordersMap[rsv_order];
        int current = _GetTableVID(orderObj);
        if (current != TABLE_VID_RESERVATION) {
            emit OrderTableChanged(TABLE_VID_RESERVATION, current, orderObj);
            m_rsv_orders.removeAll(rsv_order);
            QLOG_INFO() << "reservation " << rsv_order << " done.";
        }
    }
}

void  BillsManger::onUpdateWritePosStatus(const OrderObject *order)
{
    QLOG_INFO() << QString("update order write pos status:[%1->%2]").arg(order->order_id)
         .arg(GetOrderStatusName(order->status, order->delivery_status));

    int last = 0, current = 0;
    QJsonObject jsonObj= order->toJson();
    _OrderTableChanged(last, current, jsonObj);
    if (current != TABLE_VID_NO_DRIVER){
        OrderObject *tmpOrderObj = m_ordersMap.value(jsonObj[JSON_KEY_ORDERID].toString());
        emit OrderTableChanged(last, current, tmpOrderObj);
    }
}
