#include <iostream>
#include <memory>
#include <string>
#include <vector>
#include <conio.h>
using namespace std;
using namespace std::tr1;
#define interface struct
#define clrscr()  system("cls")
//***************************************
//     IMenuHandler
//***************************************
interface IMenuHandler
{
       virtual ~IMenuHandler() {}
       virtual void OnCommand( unsigned int id ) = 0;
};
//***************************************
//     AbstractMenu
//***************************************
class AbstractMenu
{
       string title;
public:
       AbstractMenu( string s) : title(s) {}
       virtual ~AbstractMenu() {}
       virtual string GetTitle() const { return title;}
       virtual void Command() = 0;
};
//***************************************
//     MenuItem
//***************************************
class MenuItem : public AbstractMenu
{
       unsigned int id;
       IMenuHandler* pMenuHandler;
public:
       MenuItem( string s, unsigned int i, IMenuHandler* pHandler) 
             : AbstractMenu(s), id(i), pMenuHandler(pHandler) {}
       virtual void Command() { pMenuHandler->OnCommand( id );     }
};
//***************************************
//     PopupMenu
//***************************************
class PopupMenu : public AbstractMenu
{
       typedef shared_ptr<AbstractMenu> PAbstractMenu;
       vector<PAbstractMenu> menu_list;
       bool isroot;
       typedef list<Tape*> TapeList;     // 테입 리스트를 명확하게 표현 해준다.
public:
       static string str_exit;
       static string str_goback;
       int xpos, ypos; // 현재 팝업 메뉴가 나타 날 위치로 메뉴를 뜨게 해준다.!!
public:
       PopupMenu(string s, bool b = false) : AbstractMenu( s ), isroot(b) {}
       void Append(AbstractMenu* m) { menu_list.push_back( (PAbstractMenu)m);}
       virtual void Command()
       {
             while( 1 )
             {
                    clrscr();
                    size_t cnt = menu_list.size();
                    for ( size_t i = 0; i < cnt; ++i )
                           cout << i + 1 << ". " <<
                                 menu_list[i]->GetTitle() << endl;
                    cout << cnt + 1 << ". " <<
                                 ( isroot ? str_exit:str_goback) << endl;
                    unsigned int cmd;
                    cout << "메뉴를선택하세요>> ";
                    cin >> cmd;
                    // 문자를 입력 한 경우
                    if ( cin.fail() )
                    {
                           cin.clear();
                           cin.ignore(256, '\n');
                           continue;
                    }
                    if ( cmd < 1 || cmd > cnt + 1 ) continue;
                    if ( cmd == cnt + 1 ) return ;
                    menu_list[cmd-1]->Command();
             }
       }
};
string PopupMenu::str_exit = "종료";
string PopupMenu::str_goback = "이전메뉴로";
//---------------------------------------------
// Menu Class 활용 예제..
class VideoShop : public IMenuHandler
{
       shared_ptr<PopupMenu> pMenuBar;         // video shop의 메인 메뉴
public:
       VideoShop() : pMenuBar( new PopupMenu("ROOT", true)) 
       { }
       virtual void OnCommand( unsigned int id )
       {
             cout << id << " 메뉴가선택됨" << endl;
             getch();
       }
       void InitMenu()
       {
             // initialize menu
             PopupMenu* p1 = new PopupMenu( "고객관리" );
             p1->Append( new MenuItem( "고객등록", 1, this ));
             p1->Append( new MenuItem( "고객삭제", 2, this ));
             p1->Append( new MenuItem( "기타",      3, this ));
             PopupMenu* p2 = new PopupMenu( "Tape 관리" );
             p2->Append( new MenuItem( "Tape 등록", 11, this ));
             p2->Append( new MenuItem( "Tape 삭제", 12, this ));
             p2->Append( new MenuItem( "기타",      13, this ));
             
             PopupMenu* p3 = new PopupMenu( "대여관리" );
             p3->Append( new MenuItem( "대여등록",   21, this ));
             p3->Append( new MenuItem( "반납",       22, this ));
             p3->Append( new MenuItem( "기타",       23, this ));
             pMenuBar->Append( p1 );
             pMenuBar->Append( p2 );
             pMenuBar->Append( p3 );
             PopupMenu::str_exit = "Exit";
             PopupMenu::str_goback = "Go Back";
       }
       void Start()
       {
             InitMenu();
             pMenuBar->Command();
       }
};
int main()
{
       VideoShop vs;
       vs.Start();
}
