local buffer_queue = require "buffer_queue"
local conn = require "conn"
local crypt = require "crypt"
local rc4 = require "rc4.c"

local pack_data = buffer_queue.pack_data

local DEF_MSG_HEADER_LEN = 2
local DEF_MSG_ENDIAN = "big"
local CACHE_MAX_COUNT = 100

local mt = {}
local cache_mt = {}

-------------- cache ------------------
local function cache_create()
    local raw = {
        size = 0,

        top = 0,
        cache = {},
    }
    return setmetatable(raw, {
        __index = cache_mt,
    })
end

function cache_mt:insert(data)
    local cache = self.cache
    self.top = self.top + 1
    cache[self.top] = data
    self.size = self.size + #data
    local remove_cache_value = cache[self.top - CACHE_MAX_COUNT]
    cache[self.top - CACHE_MAX_COUNT] = nil -- 只缓存最近CACHE_MAX_COUNT个包
    if remove_cache_value then
        self.size = self.size - #remove_cache_value
    end
end

function cache_mt:get(nbytes)
    if self.size < nbytes then
        return false
    end

    local cache = self.cache
    local i = self.top
    local count = 0
    local ret = {}
    while count < nbytes do
        local v = cache[i]
        local len = #v
        local n = len
        local vv = v
        if count + len > nbytes then
            local sub_n = nbytes - count
            local pos = len - sub_n
            local sub_v = string.sub(v, pos + 1)
            n = sub_n
            vv = sub_v
        end
        table.insert(ret, 1, vv)
        count = count + n
        i = i - 1
    end

    return table.concat(ret)
end

function cache_mt:clear()
    self.size = 0
    self.top = 0
end

local function dummy(...)
    print("send dummy", ...)
end

local function dispose_error(state_self, _success, _err, _status)
    local success = false
    local err = state_self.name
    local status = "reconnect_error"
    return success, err, status
end

-------------- state ------------------

local state = {
    newconnect = {
        name = "newconnect",
        request = false,
        dispatch = false,
        send = false,
        dispose = false,
    },

    reconnect = {
        name = "reconnect",
        request = false,
        dispatch = false,
        send = false,
        dispose = false,
    },

    forward = {
        name = "forward",
        request = false,
        dispatch = false,
        send = false,
        dispose = false,
    },

    reconnect_error = {
        name = "reconnect_error",
        send = dummy,
        dispose = dispose_error,
    },

    reconnect_match_error = {
        name = "reconnect_match_error",
        send = dummy,
        dispose = dispose_error,
    },

    reconnect_cache_error = {
        name = "reconnect_cache_error",
        send = dummy,
        dispose = dispose_error,
    },

    close = {
        name = "close",
        send = dummy,
        dispose = false,
    },
}

local function switch_state(self, s, ...)
    local v = state[s]
    assert(v)
    print(">>>>>>>>>>>>>switch_state:", s, ...)
    self.v_state = v
    if v.request then
        v.request(self, ...)
    end
end

local out = {}

-------------- new connect state ------------------
function state.newconnect.request(self, target_server, flag)
    -- 0\n
    -- base64(DH_key)\n
    -- targetServer\n
    -- flag

    target_server = target_server or ""
    flag = flag or 0
    local clientkey = crypt.randomkey()
    local data = string.format("0\n%s\n%s\n%d", crypt.base64encode(crypt.dhexchange(clientkey)), target_server, flag)

    data = pack_data(data, 2, "big")
    self.v_sock:send(data)
    self.v_clientkey = clientkey
    print("request:", data)
    self.v_send_buf_top = 0
end

function state.newconnect.send(self, data)
    self.v_send_buf_top = self.v_send_buf_top + 1
    self.v_send_buf[self.v_send_buf_top] = data
end

local function send(self, data)
    local _send = self.v_state.send
    _send(self, data)
    return true
end

function state.newconnect.dispatch(self)
    local data = self.v_sock:pop_msg(2, "big")

    if not data then
        return
    end

    print("dispatch:", data)
    local id, key = data:match("([^\n]*)\n([^\n]*)")

    self.v_id = tonumber(id)
    key = crypt.base64decode(key)

    local secret = crypt.dhsecret(key, self.v_clientkey)

    local rc4_key = crypt.hmac64_md5(secret, "\0\0\0\0\0\0\0\0") .. crypt.hmac64_md5(secret, "\1\0\0\0\0\0\0\0") ..
                        crypt.hmac64_md5(secret, "\2\0\0\0\0\0\0\0") .. crypt.hmac64_md5(secret, "\3\0\0\0\0\0\0\0")

    self.v_secret = secret
    self.v_rc4_c2s = rc4.rc4(rc4_key)
    self.v_rc4_s2c = rc4.rc4(rc4_key)

    switch_state(self, "forward")

    -- 发送在新连接建立中间缓存的数据包
    for i = 1, self.v_send_buf_top do
        send(self, self.v_send_buf[i])
    end
    self.v_send_buf_top = 0
    self.v_send_buf = {}
end

function state.newconnect.dispose(state_self, success, err, status)
    if success then
        return true, nil, "connect"
    else
        err = string.format("sock_error:%s sock_status:%s sconn_state:newconnect", err, status)
        return false, err, "connect"
    end
end

--------------  reconnect state ------------------
function state.reconnect.request(self)
    -- id\n
    -- index\n
    -- recvnumber\n
    -- base64(HMAC_CODE)\n

    self.v_reconnect_index = self.v_reconnect_index + 1

    local content = string.format("%d\n%d\n%d\n", self.v_id, self.v_reconnect_index, self.v_recvnumber)

    local hmac = crypt.base64encode(crypt.hmac64_md5(crypt.hashkey(content), self.v_secret))
    local data = string.format("%s%s\n", content, hmac)
    data = pack_data(data, 2, "big")

    print("request:", data)

    self.v_sock:send(data)
end

-- 在断线重连期间，仅仅是把数据插入到cache中
function state.reconnect.send(self, data)
    local rc4_c2s = self.v_rc4_c2s
    local cache = self.v_cache
    data = rc4_c2s:crypt(data)

    self.v_sendnumber = self.v_sendnumber + #data
    cache:insert(data)
end

function state.reconnect.dispatch(self)
    local data = self.v_sock:pop_msg(2, "big")

    if not data then
        return
    end

    print("dispatch:", data)
    local recv, msg = data:match "([^\n]*)\n([^\n]*)"
    recv = tonumber(recv)

    local sendnumber = self.v_sendnumber

    local cb = self.v_reconnect_cb
    self.v_reconnect_cb = nil

    -- 重连失败
    if msg ~= "200" then
        print("msg:", msg)
        if cb then
            cb(false)
        end
        switch_state(self, "reconnect_error")
        return
    end

    -- 服务器接受的数据要比客户端记录的发送的数据还要多
    if recv > sendnumber then
        if cb then
            cb(false)
        end
        switch_state(self, "reconnect_match_error")
        return
    end

    -- 需要补发的数据
    local nbytes = 0
    if recv < sendnumber then
        nbytes = sendnumber - recv
        local more_data = self.v_cache:get(nbytes)
        -- 缓存的数据不足
        if not more_data then
            if cb then
                cb(false)
            end
            switch_state(self, "reconnect_cache_error")
            return
        end

        -- 发送补发数据
        assert(#more_data == nbytes)
        self.v_sock:send(more_data)
    end

    -- 重连成功
    if cb then
        cb(true)
    end
    switch_state(self, "forward")
end

function state.reconnect.dispose(state_self, success, err, status)
    if success then
        return true, nil, "reconnect"
    else
        err = string.format("sock_error:%s sock_status:%s sconn_state:reconnect", err, status)
        return false, err, "reconnect"
    end
end

--------------  forward ------------------
function state.forward.dispatch(self)
    local recv_buf = self.v_recv_buf
    local rc4_s2c = self.v_rc4_s2c
    local sock = self.v_sock
    local count = sock:recv(out)

    for i = 1, count do
        local v = out[i]
        self.v_recvnumber = self.v_recvnumber + #v
        v = rc4_s2c:crypt(v)
        recv_buf:push(v)
    end
end

function state.forward.send(self, data)
    local sock = self.v_sock
    local rc4_c2s = self.v_rc4_c2s
    local cache = self.v_cache
    data = rc4_c2s:crypt(data)

    sock:send(data)

    self.v_sendnumber = self.v_sendnumber + #data
    cache:insert(data)
end

function state.forward.dispose(state_self, success, err, status)
    if success then
        if status ~= "forward" then
            error(string.format("invalid sock_status:%s", status))
        end
        return true, nil, "forward"
    else
        err = string.format("sock_error:%s sock_status:%s sconn_state:forward", err, status)
        return false, err, "forward"
    end
end

--------------  close ------------------
function state.close.dispose(state_self, success, err, status)
    err = string.format("sock_error:%s sock_status:%s sconn_state:close", err, status)
    return false, err, "close"
end

local function connect(host, port, targetserver, flag)
    local raw = {
        v_state = false,
        v_sock = false,

        v_clientkey = false,
        v_secret = false,
        v_id = false,

        v_rc4_c2s = false,
        v_rc4_s2c = false,

        v_sendnumber = 0,
        v_recvnumber = 0,
        v_reconnect_index = 0,
        v_cache = cache_create(),

        v_send_buf = {},
        v_send_buf_top = 0,

        v_recv_buf = buffer_queue.create(),
    }

    local sock, err = conn.connect_host(host, port)
    if not sock then
        return nil, err
    end

    raw.v_sock = sock
    local self = setmetatable(raw, {
        __index = mt,
    })
    switch_state(self, "newconnect", targetserver, flag)
    return self
end

function mt:reconnect(cb)
    local state_name = self.v_state.name
    if state_name ~= "forward" and state_name ~= "reconnect" then
        return false, string.format("error state switch `%s` to reconnect", state_name)
    end

    local addr = self.v_sock.o_host_addr
    local port = self.v_sock.o_port

    local success, err = self.v_sock:new_connect(addr, port)
    if not success then
        return false, err
    end

    self.v_reconnect_cb = cb
    switch_state(self, "reconnect")
    return true
end

--[[
update 接口现在会返回三个参数 success, err, status

success: boolean类型 表示当前status是否正常
    true: err返回值为nil
    false: err返回值为string，描述错误信息

err: string类型 表示当前status的错误信息，在success 为false才会有效

status: string类型 当前sconn所在的状态，状态只能是:
    "connect": 连接状态
    "forward": 连接成功状态
    "reconnect": 断线重连状态
    "connect_break": 断开连接状态
    "close": 关闭状态
]]
function mt:update()
    local sock = self.v_sock
    local cur_state = self.v_state
    local success, err, status = sock:update()
    local dispatch = cur_state.dispatch
    if success and dispatch then
        dispatch(self)
    end

    -- 网络连接主动断开
    if status == "connect_break" then
        return success, err, status
    end

    -- 处理返回状态值
    success, err, status = cur_state.dispose(cur_state, success, err, status)
    return success, err, status
end

function mt:send_msg(data, header_len, endian)
    local _send = self.v_state.send
    header_len = header_len or DEF_MSG_HEADER_LEN
    endian = endian or DEF_MSG_ENDIAN

    data = pack_data(data, header_len, endian)
    _send(self, data)
    return true
end

function mt:recv_msg(out_msg, header_len, endian)
    header_len = header_len or DEF_MSG_HEADER_LEN
    endian = endian or DEF_MSG_ENDIAN

    local recv_buf = self.v_recv_buf
    return recv_buf:pop_all_block(out_msg, header_len, endian)
end

function mt:close()
    if self.v_sock then
        self.v_sock:close()
    end
    self.v_sock = nil
    self.v_recv_buf:clear()
    switch_state(self, "close")
end

return {
    connect_host = connect,
}

