// restore.c
// Modified by island@fyzx

#include <ansi.h>
#include <command.h>

inherit F_CLEAN_UP;

private string get_backup_data_dir(string user, int day);
private void   clear_user_data(object ob);
int     help(object me);

int main(object me, string arg)
{
        int day;
        string user;
        string dir;
        string flogin, fuser;
        string dump_apply;
        string msg;
        object ob, link_ob;
        object item;
        int clear_flag;

        if (! SECURITY_D->valid_grant(me, "(arch)"))
                return 0;

        if (BACKUP_D->is_backuping())
        {
                write("注意：自动备份马上就要开始工作，现在不能执行恢复操作。\n");
                return 1;
        }

	if (! arg)
                return help(me);

        if (sscanf(arg, "-c %s", arg) || sscanf(arg, "%s -c", arg))
                // 调入该玩家以后清除必要的数据
                clear_flag = 1;
        else
                clear_flag = 0;

        if (sscanf(arg, "%s from %d", user, day) != 2)
        {
                if (sscanf(arg, "%s from last", user) == 1)
                        day = 1;
                else
                if (sscanf(arg, "%s from temp", user) == 1)
                        day = 0;
                else
                if (sscanf(arg, "%s from dump/%s", user, dump_apply) == 2)
                        day = -1;
                else
                {
                        user = arg;
                        day = 1;
                }
        }

        if (day < -1 || day > 7)
        {
                write("没有这个备份数据区。\n");
                return 1;
        }

	seteuid(getuid());
        if (day == -1)
        {
                // restore from dump
                dir = DUMP_DIR + dump_apply;
                if (dir[strlen(dir) - 1] != '/')
                        dir += "/";
        } else
        if (day == 0)
                // restore from temp
                dir = TEMP_DIR;
        else
                // restore from backup
                dir = get_backup_data_dir(user, day);

        flogin = "login/" + user[0..0] + "/" + user + __SAVE_EXTENSION__;
        fuser  = "user/"  + user[0..0] + "/" + user + __SAVE_EXTENSION__;

        msg = HIM "开始恢复玩家(" + user + ")的备份数据。\n\n" NOR;
        // check the backup data
        if (file_size(dir + flogin) < 0)
        {
                write(sprintf("%s缺少文件：%s，无法恢复。\n", msg, dir + flogin));
                return 1;
        }

        if (file_size(dir + fuser) < 0)
        {
                write(sprintf("%s缺少文件：%s，无法恢复。\n", msg, dir + fuser));
                return 1;
        }

        // remove current user's data
        if (file_size(DATA_DIR + flogin) >= 0 ||
            file_size(DATA_DIR + fuser) >= 0)
        {
                if (dir == TEMP_DIR)
                {
                        msg += "恢复保存在暂存区中的数据。\n";
                } else
                if (file_size(TEMP_DIR + flogin) >= 0 ||
                    file_size(TEMP_DIR + fuser) >= 0)
                {
                        msg += "由于暂存区(/temp/)下面已经存放了玩家"
                               "的数据，因此本次不作暂存。\n";
                } else
                {
                        // move current user's data to /temp/
                        assure_file(TEMP_DIR + flogin);
                        assure_file(TEMP_DIR + fuser);
                        cp(DATA_DIR + flogin, TEMP_DIR + flogin);
                        cp(DATA_DIR + fuser,  TEMP_DIR + fuser);
                        msg += "玩家目前的数据暂时保存到暂存区(/temp/)下。\n";
                }
                rm(DATA_DIR + flogin);
                rm(DATA_DIR + fuser);
                msg += "删除玩家目前的档案。\n";
        }

        // after remove, if the data existed ?
        if (file_size(DATA_DIR + flogin) >= 0 ||
            file_size(DATA_DIR + fuser) >= 0)
        {
                write(sprintf("%s没有能够成功的删除玩家现在的档案(%s)和(%s)。\n"
                       "请先查证然后再执行备份操作。\n",
                       msg, DATA_DIR + flogin, DATA_DIR + fuser));
                return 1;
        }

        // ok. copy the backup data to /data/
        assure_file(DATA_DIR + flogin);
        assure_file(DATA_DIR + fuser);
        cp(dir + flogin, DATA_DIR + flogin);
        cp(dir + fuser,  DATA_DIR + fuser);
        msg += sprintf("从(%s)中复制玩家的档案数据。\n", dir);

        // success ?
        if (file_size(DATA_DIR + flogin) < 0 ||
            file_size(DATA_DIR + fuser) < 0)
        {
                write(sprintf("%s没有能够成功的从(%s)中复制玩家的档案。\n",
                       msg, dir));
                return 1;
        }
        msg += sprintf("档案复制成功。\n");
        log_file("backup", sprintf("user %s has been restore by %s from %s.\n",
                                   user, geteuid(me), dir));

        // the player online?
        if (! (ob = find_player(user)))
        {
                write(msg);
                EXAMINE_CMD->main(me, "-u " + user);
                if (objectp(ob = find_player(user)))
                {
                        if (clear_flag)
                        {
                                tell_object(me, HIG "清除了玩家" + ob->name(1) + "的某些相关数据。\n" NOR);
                                clear_user_data(ob);
                        }
                } else
                        tell_object(me, HIG "无法载入该玩家。\n" NOR);
                return 1;
        }

        // restore the object
        tell_object(ob, HIM "\n从磁盘中载入你的数据，并重新设置。\n" NOR);
        if (! ob->restore())
        {
                write(sprintf("%s该用户无法读取备份的数据。\n", msg));
                return 1;
        }
        if (objectp(link_ob = ob->query_temp("link_ob")))
                link_ob->restore();

        write(msg);

        // re-setup the user
        foreach (item in all_inventory(ob))
        {
                if (item->is_character())
                {
                        tell_object(me, item->name(1) + "从玩家" +
                                        ob->name(1) + "身上脱离。\n");
                        item->move(environment(me), 1);
                } else
                        destruct(item);
        }

        ob->delete_temp("user_setup");
        ob->setup();
        if (! environment(ob))
        {
                tell_object(ob, HIC "\n由于你现在所处的环境不正常，需"
                                "要移动到巫师会客室。\n" NOR);
                ob->move("/d/wizard/guest_room");
                ob->set("startroom", "/d/wizard/guest_room");
        }

        // force look
        tell_object(ob, HIY  "> l\n" NOR);
        catch(ob->force_me("look"));

        // force hp
        tell_object(ob, HIY  "> hp\n" NOR);
        catch(ob->force_me("hp"));

        // force show
        ob->write_prompt();
        tell_object(ob, HIY "check backup data now\n" +
                        HIC "请检查你目前的状况是否正确。\n" NOR);
        if (ob != me)
                tell_object(me, HIC "\n玩家已经重新设置。\n\n" NOR);

        if (clear_flag)
        {
                write(HIG "清除了玩家" + ob->name(1) + "的某些相关数据。\n" NOR);
                clear_user_data(ob);
        }
	return 1;
}

// return the directory of the user
// dir/login/?/??? & dir/user/?/??? would be the user's data
private string get_backup_data_dir(string user, int day)
{
        mixed lt;
        int now;

        lt = localtime(time());
        lt[LT_MON]++;   // the month is from 0..31 so I will adjust it
        now = lt[LT_HOUR] * 100 + lt[LT_MIN];
        if (now > BACKUP_D->query_backup_time())
        {
                // because the o'clock has pass through the backuping
                // time, so the most recently backup data is of today
                day--;
        }

        lt[LT_MDAY] -= day;
        if (lt[LT_MDAY] > 0)
                return sprintf("%s%d-%d-%d/", BACKUP_DIR,
                               lt[LT_YEAR], lt[LT_MON], lt[LT_MDAY]);

        lt[LT_MON]--;
        switch (lt[LT_MON])
        {
        case 2:
                if ((lt[LT_YEAR] % 4) == 0 && (lt[LT_YEAR] % 100) != 0)
                        lt[LT_MDAY] += 29;
                else
                        lt[LT_MDAY] += 28;
                break;

        case 1: case 3: case 5: case 7: case 8: case 10:
                lt[LT_MDAY] += 31;
                break;

        case 4: case 6: case 9: case 11:
                lt[LT_MDAY] += 30;
                break;

        case 0: // last year
                lt[LT_MDAY] += 31;
                lt[LT_MON] = 12;
                lt[LT_YEAR] --;
        }

        return sprintf("%s%d-%d-%d/", BACKUP_DIR,
                       lt[LT_YEAR], lt[LT_MON], lt[LT_MDAY]);
}

private void clear_user_data(object ob)
{
        ob->delete("private_room");
        ob->delete("can_summon");
        ob->delete("couple");
        ob->delete("brothers");
        ob->delete("league");
        ob->set("balance", 0);
}

int help(object me)
{
        write(@HELP
指令格式：restore [-c] <玩家ID> from <last | 1..7 | temp | dump>

从备份区恢复一个玩家的数据。其中 last 等效于１，表示从最近一
次的备份中恢复。玩家数据恢复时，当前的数据将被保存到一个暂存
区(如果暂存区空)，如果对玩家恢复的备份不满意，可以从 temp 中
恢复。玩家的数据恢复时，如果玩家在线上，将执行 restore 操作，
如果玩家此时数据不正常：包括没有环境或是没有 setup，将重新更
新玩家。如果玩家不在线上，该命令会自动将玩家调入游戏。当你处
理完毕必要的数据以后可以将该玩家踢出(kickout)游戏。

执行玩恢复操作以后应该将玩家的数据从暂存区中清除掉。

系统 SHUTDOWN 或者崩溃以后，所有的存盘数据包括玩家数据将被保
存到 dump 目录中，这是为了防止系统重新启动以后出现故障导致数
据受到损失，因此也可以从 dump 中恢复数据。

使用 -c 参数可以在恢复玩家数据以后清除玩家所有相关性的数据。
这是针对那些自杀以后恢复得玩家而言的，由于他们自杀所以所有的
相关性数据都会被清除，恢复以后应该清除他们自身的这些数据，避
免数据不一致。

注意：在备份系统第一次提出提示以后一直到备份完毕的过程中，不
能执行有关恢复玩家数据的操作。

相关指令：cleartemp, clear
HELP );
        return 1;
}
