%% ---------------------------------------------------------------------------
%% @doc 数据库脚本升级管理
%% @author hek
%% @since  2016-09-08
%% @deprecated 本模块自动升级开发环境数据库脚本
%% ---------------------------------------------------------------------------
-module(db_upgrade).
-compile(export_all).
-include("common.hrl").

-define(VERSION_TYPE_DEV,       1). %% 版本类型：1开发服
-define(VERSION_TYPE_DEV_TEST,  2). %% 版本类型：2开发测试服(即稳定、速游测试服)

-define(SQL_SHOW_VERSION_TABLE, <<"show tables like '~p'">>).
-define(SQL_SELECT_VERSION,     <<"select `version` from `sql_version` where `type` = ~p">>).
-define(SQL_REPLACE_VERSION,    <<"replace into `sql_version`(`type`, `version`) values(~p, ~p)">>).

-ifdef(DEV_SERVER).
execute() ->
    init_mysql(),
    do_execute(?VERSION_TYPE_DEV),
    init:stop(),
    ok.
-else.
execute() ->
    case is_dev_test() of
        true ->
            init_mysql(),
            do_execute(?VERSION_TYPE_DEV_TEST);
        _ -> skip
    end,
    init:stop(),
    ok.
-endif.

is_dev_test() ->
    case init:get_argument(config) of
        {ok, [[Config|_]|_]} -> %% Config为gsrv或cls
            [_DbHost, _DbPort, _DbUser, _DbPass, _DbName, _DbEncode, _DBConns, _MaxConns, IsDevTest] = get_mysql(Config ++ ".config"),
            IsDevTest =:= 1;
        _ -> false
    end.

%% mysql数据库连接初始化
init_mysql() ->
	case init:get_argument(config) of
		{ok, [[Config|_]|_]} -> %% Config为gsrv或cls
			[DbHost, DbPort, DbUser, DbPass, DbName, DbEncode, DBConns, MaxConns, _IsDevTest] = get_mysql(Config ++ ".config"),
    		db:start(DbHost, DbPort, DbUser, DbPass, DbName, DbEncode, DBConns, MaxConns);
    	_ ->
    		?ERR("======db_upgrade init_mysql:~p~n ======", [error])
	end,
    ok.

%% 获取mysql参数
get_mysql(File) ->
    case file:consult(File) of
        {error, enoent} = Error ->
            ?ERR("init_mysql file ~p not exist error ~p ~n", [File, Error]),
            Config = [];
        {error, R} = Error ->
            ?ERR("init_mysql file error: ~p error ~p ~n", [file:format_error(R),Error]),
            Config = [];
        {ok, [Config]} -> Config
    end,
    case lists:keyfind(gsrv, 1, Config) of
    	{gsrv, ConfigList} -> skip;
    	_ -> ConfigList = []
    end,
    Host1 = case lists:keyfind(db_host, 1, ConfigList) of
                {db_host, Host} -> Host;
                _ -> "localhost"
            end,
    Port1 = case lists:keyfind(db_port, 1, ConfigList) of
                {db_port, Port} -> Port;
                _ -> 3306
            end,
    User1 = case lists:keyfind(db_user, 1, ConfigList) of
                {db_user, User} -> User;
                _ -> "root"
            end,
    Pass1 = case lists:keyfind(db_pass, 1, ConfigList) of
                {db_pass, Pass} -> Pass;
                _ -> "root"
            end,
    Name1 = case lists:keyfind(db_name, 1, ConfigList) of
                {db_name, Name} -> Name;
                _ -> "test"
            end,
    Encode1 = case lists:keyfind(db_encode, 1, ConfigList) of
                  {db_encode, Encode} -> Encode;
                  _ -> utf8
              end,
    Conns1 = case lists:keyfind(db_connections, 1, ConfigList) of
                 {db_connections, Conns} -> Conns;
                 _ -> 15
             end,
    MaxConns1 = case lists:keyfind(db_max_connections, 1, ConfigList) of
                 {db_max_connections, MaxConns} -> MaxConns;
                 _ -> 10
             end,
    IsDevTest1 = case lists:keyfind(is_dev_test, 1, ConfigList) of
                 {is_dev_test, IsDevTest} -> IsDevTest;
                 _ -> 0
             end,
    [Host1, Port1, User1, Pass1, Name1, Encode1, Conns1, MaxConns1, IsDevTest1].

do_execute(VersionType) ->
	LocalVersion = get_local_version(VersionType),
	ExecuteList = get_execute_list(VersionType, LocalVersion),
    do_execute(VersionType, ExecuteList).

do_execute(VersionType, ExecuteList) ->
	F = fun({Version, FileName}) ->
        catch ?PRINT("======version start====== ~p~n", [Version]),
        catch ?ERR("======version start====== ~p", [Version]),
        ExecuteFun = fun() -> execute_version(VersionType, Version, FileName) end,
        case db:transaction(ExecuteFun)  of
            ok ->
                catch ?PRINT("======version finish====== ~p~n", [Version]),
                catch ?ERR("======version finish====== ~p", [Version]),
                skip;
            Error ->
                catch ?ERR("execute: error ~n~p", [Error]),
                throw(Error)
        end
    end,
    lists:foreach(F, ExecuteList).

execute_version(VersionType = ?VERSION_TYPE_DEV, Version, FileName) ->
	execute_sql("../sql/"++FileName++".sql"),
    db:execute(io_lib:format(?SQL_REPLACE_VERSION, [VersionType, Version])),
	ok;
execute_version(VersionType = ?VERSION_TYPE_DEV_TEST, Version, FileName) ->
    execute_sql("../sql/test_sql/"++FileName++".sql"),
    db:execute(io_lib:format(?SQL_REPLACE_VERSION, [VersionType, Version])),
    ok.

get_execute_list(?VERSION_TYPE_DEV, LocalVersion) ->
	List = data_db_upgrade:version_list(),
	FilterList = lists:filter(fun({Ver, _Name}) -> Ver>LocalVersion end, List),
	lists:keysort(1, FilterList);
get_execute_list(?VERSION_TYPE_DEV_TEST, LocalVersion) ->
    List = data_test_db_upgrade:version_list(),
    FilterList = lists:filter(fun({Ver, _Name}) -> Ver>LocalVersion end, List),
    lists:keysort(1, FilterList).

get_local_version(VersionType) ->
	init_version(),
	get_version(VersionType).

init_version() ->
    Sql = io_lib:format(?SQL_SHOW_VERSION_TABLE, [sql_version]),
    case db:get_one(Sql) of
        <<"sql_version">> -> skip;
        _ ->
            execute_sql("../sql/version_init.sql"),
            db:execute(io_lib:format(?SQL_REPLACE_VERSION, [?VERSION_TYPE_DEV, get_now_version(?VERSION_TYPE_DEV)])),
            db:execute(io_lib:format(?SQL_REPLACE_VERSION, [?VERSION_TYPE_DEV_TEST, get_now_version(?VERSION_TYPE_DEV_TEST)]))
    end.

get_version(VersionType) ->
    NowVersion = get_now_version(VersionType),
    Sql = io_lib:format(?SQL_SELECT_VERSION, [VersionType]),
    case db:get_one(Sql) of
        null ->
            db:execute(io_lib:format(?SQL_REPLACE_VERSION, [VersionType, NowVersion])),
            NowVersion;
        Version -> Version
    end.

get_now_version(?VERSION_TYPE_DEV) ->
	case lists:reverse(lists:keysort(1, data_db_upgrade:version_list())) of
		[] -> 0;
		[{NowVersion, _Name}|_] -> NowVersion
	end;
get_now_version(?VERSION_TYPE_DEV_TEST) ->
    case lists:reverse(lists:keysort(1, data_test_db_upgrade:version_list())) of
        [] -> 0;
        [{NowVersion, _Name}|_] -> NowVersion
    end.

%% @doc 执行一个sql文件指令
execute_sql(FileName) ->
    case file:open(FileName, read) of
		{ok, IoDevice} ->
			String = read_line(FileName, IoDevice, ""),
			SqlList = re:split(String, ";"),
			F = fun(Cmd) ->
                        Sql = util:replace(Cmd, [{"\n", ""}, {"\t", ""}]),
                        case util:replace(Sql, [{" ", ""}]) of
                            <<>> -> skip;
                            [] -> skip;
                            _ -> db:execute(Sql)
                        end
                end,
			lists:foreach(F, SqlList),
            file:close(IoDevice);
		Error ->
            catch ?ERR("execute_sql file:~p  error ~p", [FileName, Error]),
            throw(Error)
    end.

%% @doc 按行读取文件，忽略注释的行
read_line(FileName, Device, Res) ->
	case file:read_line(Device) of
		{ok, Sql} ->
			case re:run(Sql, "--") of
        		nomatch -> read_line(FileName, Device, [Sql|Res]);
        		_ -> read_line(FileName, Device, Res)
        	end;
		eof -> lists:reverse(Res);
		Error ->
			catch ?ERR("execute_sql file read_line:~p  error ~p", [FileName, Error]),
			Res
	end.
