#include <MiX.h>
#include <extern/STLxx.h>
#include <extern/compose.hpp>

#include <iostream>
#include <fstream>
#include <string>
#include <utility>

typedef MiX::Element<char> Person;
typedef std::map<std::string,Person*> Index;

class PhoneBook {
  MiX::Document<char>& doc_;
  Index index_;

  MiX::Document<char>& loadDocument(std::string fname){
    std::ifstream fin(fname.c_str());
    if(!fin){
      MiX::Document<char>& ret = MiX::Document<char>::create("PhoneBook");
      MiX::Attribute<char>::create("version","1.0",ret);
      MiX::Attribute<char>::create("encoding","UTF-8",ret);
      return ret;
    }else{
      std::string ln,data;
      std::getline(fin,ln);
      while(!fin.eof()){
	data+=ln;
	std::getline(fin,ln);
      }
      fin.close();
      MiX::DomParser<char> parser;
      parser.setIgnoreSpace(true);
      return parser.parse(data.c_str());
    }
  }
  
  Index loadIndex(MiX::Document<char>& doc){
    Index ret;
    MiX::NodeList<char>::iterator it = doc.getRoot().getChildren().begin();
    MiX::NodeList<char>::iterator last = doc.getRoot().getChildren().end();    
    for( ;it!=last;++it){
      if((*it)->getType()==MiX::Node_Element){
	Person* p = dynamic_cast<Person*>(*it);
	ret.insert(std::make_pair((*p)("Name").getText(),p));
	std::cout << (*p)("Name").getText() << " Loaded." << std::endl;
      }
    }
    return ret;
  }

public:
  PhoneBook(std::string fname)
    : doc_(loadDocument(fname)),index_(loadIndex(doc_)){
  }
  void store(std::string fname){
    std::ofstream fout(fname.c_str());
    fout << doc_.toString(true) << std::endl;
    fout.close();
  }
  void createPerson(std::string name,std::string phone){
    MiX::Element<char>& e=MiX::Element<char>::create("Person",doc_.getRoot());
    MiX::Element<char>& n=MiX::Element<char>::create("Name",e);
    MiX::Element<char>& p=MiX::Element<char>::create("Phone",e);
    n.setText(name);
    p.setText(phone);
    index_.insert(std::make_pair(name,&e));
  }
  Index& getIndex(){
    return index_;
  }
};

void outputPerson(std::ostream& os,Person& p){
  os << p("Name").getText() << " : " << p("Phone").getText() << std::endl;
}

std::string input(const char* prompt){
  std::string ret;
  std::cout << prompt << ": " << std::flush;
  std::cin >> ret;
  return ret;
}

char prompt(){
  std::string s = input("add/delete/find/list/clear/quit [a,d,f,l,c,q]");
  return s.at(0);
}

void error(const char* errmsg){
  std::cout << "Error: " << errmsg << std::endl;
}

int main(){
  PhoneBook pb("phonebook.xml");

  char cmd = '\0';
  while(cmd!='q'){
    cmd = prompt();
    switch(cmd){
    case 'a' : case 'A' : {
      std::string name = input("Name");
      Index::iterator it = pb.getIndex().find(name);
      if(it!=pb.getIndex().end()) error("already exists.");
      else {
	std::string phone = input("Phone");
	pb.createPerson(name,phone);
      }
      break;
    }
    case 'd' : case 'D' : {
      std::string name = input("Name");
      Index::iterator it  =pb.getIndex().find(name);   
      if(it==pb.getIndex().end()){
	error("not found!");
      }else{
	it->second->destroy();
	pb.getIndex().erase(it);
      }
      break;
    }
    case 'f' : case 'F' : {
      std::string name = input("Name");
      Index::iterator it=pb.getIndex().find(name);
      if(it!=pb.getIndex().end()) 
	outputPerson(std::cout,*(it->second));
      else error("not found.");
      break;
    }
    case 'l' : case 'L' : {
      std::cout << pb.getIndex().size() <<"entries:" << std::endl;
      Index::iterator it = pb.getIndex().begin();
      Index::iterator last = pb.getIndex().end();
      for( ;it!=last;++it) outputPerson(std::cout,*(it->second));
      break;
    }
    case 'c' : case 'C' : {
      Index index = pb.getIndex();
      Index::iterator it = index.begin();
      Index::iterator last = index.end();
      for( ;it!=last;++it){ 
	it->second->destroy();
	pb.getIndex().erase(it);
      }
      break;
    }
    // ½ªÎ»
    case 'q' : case 'Q' : 
    default :
      break;
    }
  }
  pb.store("phonebook.xml");
}