/**
 * Tencent is pleased to support the open source community by making MSEC available.
 *
 * Copyright (C) 2016 THL A29 Limited, a Tencent company. All rights reserved.
 *
 * Licensed under the GNU General Public License, Version 2.0 (the "License"); 
 * you may not use this file except in compliance with the License. You may 
 * obtain a copy of the License at
 *
 *     https://opensource.org/licenses/GPL-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the 
 * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
 * either express or implied. See the License for the specific language governing permissions
 * and limitations under the License.
 */


#include "micro_thread.h"
#include "mt_notify.h"
#include "mt_connection.h"
#include "mt_session.h"
#include "mt_action.h"

using namespace std;
using namespace NS_MICRO_THREAD;

void IMtAction::Init()
{
    _flag       = MULTI_FLAG_UNDEF;
    _proto      = MT_UDP;
    _conn_type  = CONN_TYPE_SHORT;
    _errno      = ERR_NONE;
    _time_cost  = 0;
    _buff_size  = 0;
    _msg        = NULL;
    _conn       = NULL;
    _ntfy_name  = 0;
    memset(&_addr, 0, sizeof(_addr));
}

void IMtAction::Reset()
{
    bool force_free = false;
    if (_errno != ERR_NONE) {
        force_free = true;
    }

    if (_conn) {
        ConnectionMgr::Instance()->FreeConnection(_conn, force_free);
        _conn = NULL;
    }
}

KqueuerObj* IMtAction::GetNtfyObj() {
    IMtConnection* conn = GetIConnection();
    if (conn) {
        return conn->GetNtfyObj();
    } else {
        return NULL;
    }
};

int IMtAction::InitConnEnv()
{
    MtFrame* mtframe = MtFrame::Instance();
    ConnectionMgr* connmgr = ConnectionMgr::Instance();
    MsgBuffPool* msgmgr = MsgBuffPool::Instance();
    NtfyObjMgr* ntfymgr = NtfyObjMgr::Instance();
    SessionMgr* sessionmgr = SessionMgr::Instance();

    if (_conn != NULL) {
        MTLOG_ERROR("Action init failed, maybe action reused in actionlist, check it!!");
        return -100;
    }

    CONN_OBJ_TYPE conn_obj_type = OBJ_CONN_UNDEF;
    NTFY_OBJ_TYPE ntfy_obj_type = NTFY_OBJ_UNDEF;
    
    MULTI_PROTO proto = this->GetProtoType();
    MULTI_CONNECT type = this->GetConnType();
    if ((MT_UDP == proto) && (CONN_TYPE_SESSION == type))   // UDP session
    {
        conn_obj_type = OBJ_UDP_SESSION;
        ntfy_obj_type = NTFY_OBJ_SESSION;
    }
    else if (MT_UDP == proto)  // UDP
    {   
        conn_obj_type = OBJ_SHORT_CONN;
        ntfy_obj_type = NTFY_OBJ_THREAD;
    }
    else    // TCP
    {
        conn_obj_type = OBJ_TCP_KEEP;
        ntfy_obj_type = NTFY_OBJ_THREAD;
    }

    _conn = connmgr->GetConnection(conn_obj_type, this->GetMsgDstAddr());
    if (!_conn) {
        MTLOG_ERROR("Get conn failed, type: %d", conn_obj_type);
        return -1;
    }
    _conn->SetIMtActon(this);

    int max_len = this->GetMsgBuffSize();
    MtMsgBuf* msg_buff = msgmgr->GetMsgBuf(max_len);
    if (!msg_buff) {
        MTLOG_ERROR("Maybe no memory, buffsize: %d, get failed", max_len);
        return -2;
    }
    msg_buff->SetBuffType(BUFF_SEND);
    _conn->SetMtMsgBuff(msg_buff);

    KqueuerObj* ntfy_obj = ntfymgr->GetNtfyObj(ntfy_obj_type, _ntfy_name);
    if (!ntfy_obj) {
        MTLOG_ERROR("Maybe no memory, ntfy type: %d, get failed", ntfy_obj_type);
        return -3;
    }
    _conn->SetNtfyObj(ntfy_obj);

    MicroThread* thread = mtframe->GetActiveThread();
    ntfy_obj->SetOwnerThread(thread);
    this->SetIMsgPtr((IMtMsg*)thread->GetThreadArgs());
    if (conn_obj_type == OBJ_UDP_SESSION)
    {
        this->SetOwnerThread(thread);
        this->SetSessionConn(_conn);
        this->SetSessionId(sessionmgr->GetSessionId());
        sessionmgr->InsertSession(this);
    }
    
    return 0;
}

int IMtAction::DoEncode()
{
    MtMsgBuf* msg_buff = NULL;
    if (_conn) {
        msg_buff = _conn->GetMtMsgBuff();
    }
    if (!_conn || !msg_buff) {
        MTLOG_ERROR("conn(%p) or msgbuff(%p) null", _conn, msg_buff);
        return -100;
    }

    int msg_len = msg_buff->GetMaxLen();
    int ret = this->HandleEncode(msg_buff->GetMsgBuff(), msg_len, _msg);
    if (ret < 0)
    {
        MTLOG_DEBUG("handleecode failed, ret %d", ret);
        return ret;
    }
    msg_buff->SetMsgLen(msg_len);

    return 0;
}

int IMtAction::DoInput()
{
    MtMsgBuf* msg_buff = NULL;
    if (_conn) {
        msg_buff = _conn->GetMtMsgBuff();
    }
    if (!_conn || !msg_buff) {
        MTLOG_ERROR("conn(%p) or msgbuff(%p) null", _conn, msg_buff);
        return -100;
    }

    int msg_len = msg_buff->GetHaveRcvLen();
    int ret = this->HandleInput(msg_buff->GetMsgBuff(), msg_len, _msg);
    if (ret < 0)
    {
        MTLOG_DEBUG("HandleInput failed, ret %d", ret);
        return ret;
    }

    return ret;
}

int IMtAction::DoProcess()
{
    MtMsgBuf* msg_buff = NULL;
    if (_conn) {
        msg_buff = _conn->GetMtMsgBuff();
    }
    if (!_conn || !msg_buff) {
        MTLOG_ERROR("conn(%p) or msgbuff(%p) null", _conn, msg_buff);
        return -100;
    }

    int ret = this->HandleProcess(msg_buff->GetMsgBuff(), msg_buff->GetMsgLen(), _msg);
    if (ret < 0)
    {
        MTLOG_DEBUG("handleprocess failed, ret %d", ret);
        return ret;
    }

    return 0;

}

int IMtAction::DoError()
{
    return this->HandleError((int)_errno, _msg);
}

IMtAction::IMtAction()
{
    Init();    
}

IMtAction::~IMtAction()
{
    Reset();
    Init();
}