// restore.c

#include <ansi.h>
#include <localtime.h>

inherit F_CLEAN_UP;

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

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

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

        if (me!=this_player(1))
                return notify_fail("你没有权力使用这个指令。\n");

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

        if (!arg)
                return help(me);

        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", user) == 1)
                        day = -1;
                else
                {
                        user = arg;
                        day = 1;
                }
        }

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

        seteuid(getuid());
        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, me->query("id"), dir));

        // the player online?
        if (! (ob = find_player(user)))
        {
                write(msg);
                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
        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");
        }
        tell_object(ob, NOR"\n>" HIY  "l\n" NOR);
        catch(ob->force_me("look"));
        tell_object(ob, NOR">" HIY  "hp\n" NOR);
        catch(ob->force_me("hp"));
        tell_object(ob, ">" HIY "check backup data now\n" +
                        HIC "请检查你目前的状况是否正确。\n" NOR +
                        (ob == me ? "" : ">"));
        if (ob != me)
                tell_object(me, HIC "\n玩家已经重新设置。\n\n" NOR);
        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;

        if (day == 0)
                // restore from temp
                return TEMP_DIR;

        if (day == -1)
//                   restore from dump
                    return DUMP_DIR;

        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 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]);
}

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

从备份区恢复一个玩家的数据。其中last等效于１，表示从最近一次的备份中
恢复。玩家数据恢复时，当前的数据将被保存到一个暂存区(如果暂存区空)，
如果对玩家恢复的备份不满意，可以从temp中恢复。玩家的数据恢复时，如果
玩家在线上，将执行restore 操作，如果玩家此时数据不正常：包括没有环境
或是没有setup，将重新更新玩家。

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

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

注意：在备份系统第一次提出提示以后一直到备份完毕的过程中，不能执行有
关恢复玩家数据的操作?
相关指令：cleartem
HELP
        );
     return 1;
}
