/**************************************************************
 * FFTacticsMUD : admin.cpp                                   *
 **************************************************************
 * (c) 2002 Damien Dailidenas (Trenton). All rights reserved. *
 **************************************************************/

#include <iomanip>
#include <strstream>
#include <fstream>
#include <cstdio>
#include <unistd.h>
#include "main.h"
#include "mysql/mysql.h"

const struct wiznet_type wiznet_table[] = {
  { "on",     WIZ_ON     },
  { "links",  WIZ_LINKS  },
  { "spam",   WIZ_SPAM   },
  { "deaths", WIZ_DEATHS },
  { "memory", WIZ_MEMORY },
  {}
};

bool CH::is_admin() {
  MYSQL mysql;   
  MYSQL_RES *res;
  MYSQL_ROW row;
  mysql_init(&mysql);
      
  if(!mysql_real_connect(&mysql, DB_LOC, DB_USER, DB_PASS, DB_NAME, 0, NULL, 0))
    return false;
      
  ostrstream ost;
  ost << "SELECT * FROM admins WHERE name='" + name + "'" << ends;
  mysql_query(&mysql, ost.str());
  res = mysql_store_result(&mysql);
    
  if(mysql_num_rows(res)) {
    row = mysql_fetch_row(res);
    wiznet = db_read_flags(row[1]);
    mysql_free_result(res);
    mysql_close(&mysql);
    return true;
  }
 
  mysql_free_result(res);
  mysql_close(&mysql);
  return false;
}

long wiznet_lookup(const string name) {
  for(short flag = 0; wiznet_table[flag].name; ++flag)
    if(find(name, wiznet_table[flag].name))
      return flag;

  return -1;
}

void do_wiznet(CH *ch, string argument = "") {
  if(argument.empty()) {
    ch->printf("Usage: wiznet [on | off | show | status | [option]]\n\r");
    return;
  }
  
  if(find(argument, "on")) {
    ch->printf("wiznet> on\n\r");
    SET_BIT(ch->wiznet, WIZ_ON);
    return;
  }
  else if(find(argument, "off")) {
    ch->printf("wiznet> off\n\r");
    REM_BIT(ch->wiznet, WIZ_ON);
    return;
  }

  short flag;  
  string str;

  if(find(argument, "status"))  {
    if(!IS_SET(ch->wiznet, WIZ_ON))
      str = "off ";
    
    for(flag = 0; wiznet_table[flag].name; ++flag)
      if(IS_SET(ch->wiznet, wiznet_table[flag].flag))
	str += (string)wiznet_table[flag].name + " ";
    
    str += ENDL;
    ch->printf("wiznet> status> " + str);
    return;
  }
  
  if(find(argument, "show")) {
    for(flag = 0; wiznet_table[flag].name; ++flag)
      str += (string)wiznet_table[flag].name + " ";

    str += ENDL;
    ch->printf("wiznet> options> " + str);
    return;
  }

  flag = wiznet_lookup(argument);
  
  if(flag == -1) {
    ch->printf("wiznet: ERROR: No such option, " + argument + ".\n\r");
    return;
  }
  
  if(IS_SET(ch->wiznet, wiznet_table[flag].flag)) {
    ch->printf("wiznet> " + (string)wiznet_table[flag].name + "> off\n\r");
    REM_BIT(ch->wiznet, wiznet_table[flag].flag);
  }
  else {
    ch->printf("wiznet> " + (string)wiznet_table[flag].name + "> on\n\r");
    SET_BIT(ch->wiznet, wiznet_table[flag].flag);
  }
  
  return;
}

void wiz_echo(const string str, long flag = 0) {
  for(DESC *d = desc_list; d; d = d->next)
    if(d->ch && d->connected == CON_PLAYING && IS_SET(d->ch->wiznet, WIZ_ON) && (!flag || IS_SET(d->ch->wiznet, flag)))
      d->ch->printf(str);
  
  return;
}

void do_disconnect(CH *ch, string argument = "") {
  string arg;
  DESC *d;
  CH *victim;
  split(argument, arg);

  if(arg.empty()) {
    ch->printf("Usage: disconnect [name]\n\r");
    return;
  }
  
  if(is_num(arg)) {
    short desc = atoi(arg.c_str());

    for(d = desc_list; d; d = d->next) {
      if(d->desc == desc) {
	d->close_socket();
	return;
      }
    }
  }
  
  if(!(victim=get_ch(arg))) {
    ch->printf("disconnect: ERROR: " + arg + " not found.\n\r");
    return;
  }
  
  for(d = desc_list; d; d = d->next ) {
    if(d == victim->desc) {
      d->close_socket();
      return;
    }
  }
  
  ch->printf("disconnect: ERROR: Descriptor not found.\n\r");
  return;
}

void do_echo(CH *ch, string argument = "") {
  if(argument.empty()) {
    ch->printf("Usage: echo [msg]\n\r");
    return;
  }
  
  for(DESC *d = desc_list; d; d = d->next)
    if(d->connected == CON_PLAYING)
      d->printf(argument + ENDL);

  return;
}

void do_reboot(CH *ch, string argument = "") {
  reboot();
  return;
}

void reboot() {
  extern bool game_down;
  log_string("Rebooting...");

  for(DESC *d = desc_list, *d_next; d; d = d_next) {
    d_next = d->next;
    d->printf("Rebooting...\n\r");
    d->ch->save();
    d->close_socket();
  }

  game_down = true;
  return;
}

void do_shutdown(CH *ch, string argument = "") {
  extern bool game_down;
  append_file(ch, "../shutdown.txt");
  game_down = true;
  
  for(DESC *d = desc_list, *d_next; d; d = d_next) {
    d_next = d->next;
    d->close_socket();
  }
  
  return;
}

void do_snoop(CH *ch, string argument = "") {
  DESC *d;
  CH *victim=NULL;
  
  if(argument.empty()) {
    ch->printf("Usage: snoop [name]\n\r");
    return;
  }
  
  if(argument != "self" && !(victim = get_ch(argument))) {
    ch->printf("snoop: ERROR: " + argument + " not found.\n\r");
    return;
  }
  
  if(argument == "self")
    victim = ch;
  
  if(!victim->desc) {
    ch->printf("snoop: ERROR: No descriptor to snoop.\n\r");
    return;
  }
  
  if(victim == ch) {
    ch->snooping = false;
    
    for(d = desc_list; d; d = d->next)
      if(d->snoop_by == ch->desc)
	d->snoop_by = NULL;
    
    return;
  }
  
  if(victim->desc->snoop_by) {
    ch->printf("snoop: ERROR: Already snooping.\n\r");
    return;
  }
  
  if(ch->desc) {
    for(d = ch->desc->snoop_by; d; d = d->snoop_by) {
      if(d->ch == victim) {
	ch->printf("snoop: ERROR: No snoop loops.\n\r");
	return;
      }
    }
  }
  
  victim->desc->snoop_by=ch->desc;
  ch->snooping=true;
  return;
}

void do_freeze(CH *ch, string argument = "") {
  CH *victim;
  
  if(argument.empty()) {
    ch->printf("Usage: freeze [target]\n\r");
    return;
  }
  
  if(!(victim = get_ch(argument))) {
    ch->printf("freeze: ERROR: Target not found.\n\r");
    return;
  }
  
  if(IS_SET(victim->act, PLR_FROZEN)) {
    REM_BIT(victim->act, PLR_FROZEN);
    ch->printf("freeze: removed\n\r");
  }
  else {
    SET_BIT(victim->act, PLR_FROZEN);
    ch->printf("freeze: set\n\r");
  }

  victim->save();
  return;
}

void do_log(CH *ch, string argument = "") {
  CH *victim;
  
  if(argument.empty()) {
    ch->printf("Usage: log [name]\n\r");
    return;
  }
  
  if(!(victim = get_ch(argument))) {
    ch->printf("log: ERROR: " + argument + " not found.\n\r");
    return;
  }
  
  if(IS_SET(victim->act, PLR_LOG)) {
    REM_BIT(victim->act, PLR_LOG);
    ch->printf("log: " + victim->name + ": enabled\n\r");
  }
  else {
    SET_BIT(victim->act, PLR_LOG);
    ch->printf("log: " + victim->name + ": disabled\n\r");
  }
  
  return;
}

void do_wizlock(CH *ch, string argument = "") {
  extern bool wizlock;
  wizlock = !wizlock;
  ch->printf("wizlock: " + (string)(wizlock ? "on" : "off") + ENDL);
  return;
}

void do_newlock(CH *ch, string argument = "") {
  extern bool newlock;  
  newlock = !newlock;
  ch->printf("newlock: " + (string)(newlock ? "on" : "off") + ENDL);
  return;
}

const struct connected_type connected_table[] = {
  { "PLAYING"                 	},
  { "GET_NAME"                	},
  { "GET_OLD_PASSWORD"        	},
  { "CONFIRM_NEW_NAME"        	},
  { "GET_NEW_PASSWORD"        	},
  { "CONFIRM_NEW_PASSWORD"	},
  { "BREAK_CONNECT"           	},
  { "FINISH"                  	},
  { "COPYOVER_RECOVER"        	},
  { "GET_SEX"                 	},
  { "GET_PK",			},
  { "GET_EMAIL",		},
  {}
};

void do_sockets(CH *ch, string argument = "") {
  ostrstream ost;
  ost << setw(2) << "ID  " << setiosflags(ios::left) << setw(12) << "NAME" << "  " << setiosflags(ios::left) << setw(20) << "STATUS" << "  " << setiosflags(ios::left) << setw(16) << "HOST" << endl;

  for(DESC *d = desc_list; d; d = d->next)
    ost << setw(2) << d->desc << "  " << setiosflags(ios::left) << setw(12) << (d->ch ? d->ch->name.c_str() : "--") << "  " << setiosflags(ios::left) << setw(20) << d->conn_status().c_str() << "  " << d->host << endl;

  ost << ends;
  ch->printf(ost.str());
  return;
}

void do_force(CH *ch, string argument = "") {
  string arg;
  split(argument, arg);

  if(arg.empty() || argument.empty()) {
    ch->printf("Usage: force [name] [command]\n\r");
    return;
  }

  if(arg == "all") {
    for(CH *vch = ch_list, *vch_next; vch; vch = vch_next) {
      vch_next = vch->next;
      vch->interpret(argument);
    }
  }
  else {
    CH *victim;

    if(!(victim = get_ch(arg))) {
      ch->printf("force: ERROR: " + arg + " not found.\n\r");
      return;
    }

    victim->interpret(argument);
  }

  return;
}

void do_invis(CH *ch, string argument = "") {
  if(IS_INVIS(ch)) {
    ch->printf("invis> off\n\r");
    REM_BIT(ch->act, PLR_INVIS);
  }
  else {
    ch->printf("invis> on\n\r");
    SET_BIT(ch->act, PLR_INVIS);
  }
  
  return;
}

void do_omnipotent(CH *ch, string argument = "") {
  if(IS_OMNIPOTENT(ch)) {
    ch->printf("omnipotent> off\n\r");
    REM_BIT(ch->act, PLR_OMNIPOTENT);
  }
  else {
    ch->printf("omnipotent> on\n\r");
    SET_BIT(ch->act, PLR_OMNIPOTENT);
  }
  
  return;
}

#define COPYOVER_FILE "copyover.data"
#define EXE_FILE      "../bin/FFTacticsMUD_exe"

void save_global_settings() {
  extern bool wizlock, newlock;
  ostrstream ost;
  ost << "UPDATE global_settings SET wizlock=" << wizlock << ", newlock=" << newlock << ends;
  mysql(NULL, ost.str());
  return;
}

void do_copyover(CH *ch, string argument = "") {
  ofstream file(COPYOVER_FILE, ios::trunc);
  extern int port, control;

  if(!file.is_open()) {
    ch->printf("Copyover file not writeable, aborted.\n\r");
    ostrstream ost;
    ost << "Could not write to copyover file: " << COPYOVER_FILE << ends;
    log_string(ost.str());
    perror("do_copyover:fopen");
    return;
  }

  if(ch)
    log_string("Copyover by " + ch->name);

  for(DESC *d = desc_list, *d_next; d; d = d_next) {
    d_next = d->next;  
    d->write("updating changes...");
    
    if(!d->ch || d->connected > CON_PLAYING)
      d->close_socket();
    else {
      file << d->desc << " " << d->ch->name << " " << d->host << endl;
      zap(d->ch);
    }
  }

  file << "-1" << endl;
  file.close();
  save_global_settings();
  ostrstream ost_port, ost_control;
  ost_port << port << ends;
  ost_control << control << ends;
  execl(EXE_FILE, "./FFTacticsMUD_exe", ost_port.str(), "copyover", ost_control.str(), (char *)NULL);
  perror("do_copyover: execl");
  ch->printf("Copyover FAILED!\n\r");
  return;
}

void copyover_recover() {
  string name, host;
  int desc;
  log_string("Copyover recovery initiated");
  ifstream file(COPYOVER_FILE);
    
  if(!file.is_open()) {
    perror("copyover_recover: fopen");
    log_string("Copyover file not found. Exitting.\n\r");
    exit(1);
  }
      
  for(;;) {
    file >> desc >> name >> host;

    if(desc == -1)
      break;

    if(!write_to_desc(desc, ".")) {
      close(desc);
      continue;
    }

    DESC *d = new DESC;
    d->desc = desc; 
    d->host = host;
    d->connected = CON_COPYOVER_RECOVER;
  
    if(!d->load_ch(name)) {
      d->write("\n\rYour character was lost in the copyover, recreate.\n\r");
      d->close_socket();
    }
    else {
      d->write("done.");
      d->ch->printf(CLEAR);
      d->ch->next = ch_list;
      ch_list = d->ch;
      d->connected = CON_PLAYING;
      d->ch->to(d->ch->area->room[ROOM_OUTSIDE]);
      d->ch->socket = host;
      d->ch->login = str_time().substr(0, 3) + str_time().substr(11, 5);
      d->ch->check_battle();
    }
  }
    
  file.close();
  log_string("Copyover complete.");
  return;
}

void do_finger(CH *ch, string argument = "") {
  MYSQL mysql;
  MYSQL_RES *res;

  if(argument.empty()) {
    ch->printf("Usage: finger [name]\n\r");
    return;
  }

  mysql_init(&mysql);

  if(!mysql_real_connect(&mysql, DB_LOC, DB_USER, DB_PASS, DB_NAME, 0, NULL, 0))
    return;

  ostrstream ost;
  ost << "SELECT name, pwd, email, created, llogoff, sex, money, FROM players WHERE name='" << argument << "'" << ends;
  mysql_query(&mysql, ost.str());
  res = mysql_store_result(&mysql);

  if(mysql_num_rows(res)) {
    mysql_free_result(res);
    mysql_close(&mysql);
    mysql_print_res(ch, ost.str());
    return;
  }
  else
    ch->printf("finger: ERROR: " + argument + " not found.\n\r");

  mysql_free_result(res);
  mysql_close(&mysql);
  return;
}

void do_tell(CH *ch, string argument = "") {
  string arg;
  CH *victim;

  split(argument, arg);

  if(argument.empty() || arg.empty()) {
    ch->printf("Usage: tell [name] [msg]\n\r");
    return;
  }

  if(!(victim = get_ch(arg))) {
    ch->printf("tell: ERROR: " + arg + " not found.\n\r");
    return;
  }

  ch->printf("{2You tell " + victim->name + " '" + argument + "{2'{0\n\r");
  victim->printf("{2" + ch->name + " tells you '" + argument + "{2'{0\n\r");
  return;
}

void do_delplayers(CH *ch, string argument = "") {
  MYSQL mysql;
  MYSQL_RES *res;

  if(argument.empty()) {
    ch->printf("Usage: deleteplayers (where llogoff =) [YYYY/MM/D]\n\r");
    return;
  }

  mysql_init(&mysql);
 
  if(!mysql_real_connect(&mysql, DB_LOC, DB_USER, DB_PASS, DB_NAME, 0, NULL, 0))
    return;

  ostrstream ost;
  ost << "SELECT * FROM players WHERE llogoff LIKE '" << argument << "%'" << ends; 
  mysql_query(&mysql, ost.str());
  res = mysql_store_result(&mysql);
  ost.freeze(false);
  ost.seekp(0, ios::beg);
  ost << (int)mysql_num_rows(res) << " players deleted.\n\r" << ends;
  ch->printf(ost.str());
  mysql_free_result(res);
  ost.freeze(false);
  ost.seekp(0, ios::beg);
  ost << "DELETE FROM players WHERE llogoff LIKE '" << argument << "%'" << ends;
  mysql_query(&mysql, ost.str());
  mysql_close(&mysql);
  return;
}

void mysql(CH *ch, const string query) {
  if(ch && find("select", query)) {
    mysql_print_res(ch, query);
    return;
  }

  MYSQL mysql;
  mysql_init(&mysql);
 
  if(!mysql_real_connect(&mysql, DB_LOC, DB_USER, DB_PASS, DB_NAME, 0, NULL, 0))
    return;

  ostrstream ost;
  ost << query << ends;
  mysql_query(&mysql, ost.str());
  mysql_close(&mysql);
  return;
}

void do_mysql(CH *ch, string argument = "") {
  if(argument.empty()) {
    ch->printf("Usage: mysql [query]\n\r");
    return;
  }

  mysql(ch, argument);
  return;
}

void do_beep(CH *ch, string argument = "") {
  CH *victim;

  if(!(victim = get_ch(argument))) {
    ch->printf("beep: ERROR: " + argument + " not found.\n\r");
    return;
  }
 
  ch->printf("\aYou beep " + victim->name + ".\n\r");
  victim->printf("\a" + ch->name + " beeps you.\n\r");
  return;
}

void do_chlist(CH *ch, string argument="") {
  CH *x;
  short cnt = 0;
  ch->printf("%2s  %-15s  %7s  %7s  %2s  %2s  %2s  %s  %3s  %2s  %2s\n\r", "ID", "NAME", "HP", "MP", "PA", "MA", "Sp", "M", "Ev", "Br", "Fa");
 
  for(x = ch_list; x; x = x->next)
    ch->printf("%2d  %-15s  %3d/%3d  %3d/%3d  %2d  %2d  %2d  %d  %2d%%  %2d  %2d\n\r", cnt++, x->name.c_str(), x->HP[0], x->HP[1], x->MP[0], x->MP[1], x->PA, x->MA, x->Sp, x->Mv, x->Ev, x->Br, x->Fa);

  return;
}   

void do_createobj(CH *ch, string argument="") {
  OBJ *obj;
   
  if(argument.empty() || !(obj = create_obj(argument))) {
    ch->printf("Usage: object create [name]\n\r");
    return;
  }
 
  obj->to(ch);
  ch->printf("Object " + obj->name() + " created.\n\r");
  return;
}

void do_destroy(CH *ch, string argument="") {
  OBJ *obj;

  if(argument.empty() || !(obj = ch->obj(argument, false))) {
    ch->printf("Usage: object destroy [name]\n\r");
    do_items(ch);
    return;
  }
    
  ch->printf("Object " + obj->name() + " destroyed.\n\r");
  zap(obj);
  return;
}

void do_disable(CH *ch, string argument = "") {
  MYSQL mysql;
  MYSQL_RES *res;
    
  if(argument.empty()) {
    ch->printf("Usage: disable [name]\n\r");
    return;
  }
 
  mysql_init(&mysql);

  if(!mysql_real_connect(&mysql, DB_LOC, DB_USER, DB_PASS, DB_NAME, 0, NULL, 0))
    return;
    
  ostrstream ost;
  ost << "SELECT name FROM players WHERE name='" << argument << "'" << ends;
  mysql_query(&mysql, ost.str());
  res = mysql_store_result(&mysql);
 
  if(mysql_num_rows(res)) {
    mysql_free_result(res);
    mysql_close(&mysql);
    ost.freeze(false);
    ost.seekp(0, ios::beg);
    ost << "UPDATE players SET disabled=1 WHERE name='" << argument << "'" << ends;
    ::mysql(ch, ost.str());
    ch->printf("Account (" + argument + ") has been disabled.\n\r");
    return;
  }
  else
    ch->printf("disable: ERROR: Account not found.\n\r");
    
  mysql_free_result(res);
  mysql_close(&mysql);
  return;
}

void do_enable(CH *ch, string argument = "") {
  MYSQL mysql;
  MYSQL_RES *res;
    
  if(argument.empty()) {
    ch->printf("Usage: enable [name]\n\r");
    return;
  }
 
  mysql_init(&mysql);
    
  if(!mysql_real_connect(&mysql, DB_LOC, DB_USER, DB_PASS, DB_NAME, 0, NULL, 0))
    return;
    
  ostrstream ost;
  ost << "SELECT name FROM players WHERE name='" << argument << "'" << ends;
  mysql_query(&mysql, ost.str());
  res = mysql_store_result(&mysql);
  
  if(mysql_num_rows(res)) {
    mysql_free_result(res);
    mysql_close(&mysql);
    ost.freeze(false);
    ost.seekp(0, ios::beg);
    ost << "UPDATE players SET disabled=0 WHERE name='" << argument << "'" << ends;
    ::mysql(ch, ost.str());
    ch->printf("Account (" + argument + ") has been enabled.\n\r");
    return;
  } 
  else
    ch->printf("enable: ERROR: Account not found.\n\r");
    
  mysql_free_result(res);  
  mysql_close(&mysql);
  return;
}