// 책임의 전가- Chain of reponsibility
// MFC의 VIEW- DOC - FRAME - APP 로 명령을 넘겨주는 것과 같은 원리..
#include <iostream>
#include <conio.h>
#include <string>
#include <vector>
using namespace std;
// 메뉴를 처리하고 싶은 모든 클래스는 아래 추상클래스에서 상속되어야 한다.
class IMenuHandler
{
       IMenuHandler* next;
public:
       IMenuHandler() : next(0) {}
       // 다음 처리할 객체를 지정한다.
       void SetNext( IMenuHandler* p ) { next = p; }
    void MenuCommand( int id )
       {
             // 먼저 자신이 처리를 시동한다.
             if ( Resolve( id ) == false ) // 처리를 못한 경우
             {
                    // 다음으로전달
                    if ( next != 0 ) next->MenuCommand( id );
             }
       }
protected:
       virtual bool Resolve( int id ) = 0;
};
//-------------------------------------------------------
#define clrscr()    system("cls")
class AbstractMenu
{
       string title;
public:
       AbstractMenu( string s ) : title(s) { }
       virtual string GetTitle() const { return title; }
       virtual void Command() = 0;   // 순수가상함수- 자식은 꼭 만들어야 한다.
};
class PopupMenu : public AbstractMenu
{
       vector<AbstractMenu*> menu_list; // Composite
public:
       PopupMenu(string s) : AbstractMenu(s) { }
       void Add( AbstractMenu* p ) { menu_list.push_back( p ); }
       void Remove() { }
       virtual string GetTitle() const
       {
             return string("[") + AbstractMenu::GetTitle() + string("]");
       }
       // 팝업메뉴가 선택 되었을때를 처리한다.
       virtual void Command()
       {
             while(1)
             {
                    // 화면을 지우고 자신의 하위메뉴를 열거해준다.
                    clrscr();
                    for( int i = 0; i < menu_list.size(); ++i )
                    {
                         cout<<i + 1<<"."<< menu_list[i]->GetTitle() <<endl;
                    }
                    cout << menu_list.size() + 1 << ". 이전메뉴로" << endl;
                    //-----------------------------------------------
                    int cmd;
                    cout << "메뉴를 생성하세요>> ";
                    cin >> cmd;
                    if ( cmd == menu_list.size()+1 ) // 이전메뉴로 선택
                           break;  // 루프탈출해서 자신을 이 함수를  마치고
                                   // 자신을 호출한 부분으로 돌아간다.
if ( cmd <= 0 || cmd > menu_list.size() )
                           continue;
// 해당메뉴를 실행한다.
                    menu_list[cmd-1]->Command();
             }
       }
};
// 메뉴를 처리하고 싶은 모든 클래스는 아래 인터페이스를 구현해야 한다.
class MenuItem : public AbstractMenu
{
       int id;
       IMenuHandler* pMenuHandler;
public:
       // 부모의 디폴트 생성자가 없으므로 여기서 초기화 해준다.
       MenuItem( string s, int n, IMenuHandler* p )
             : AbstractMenu(s), id(n), pMenuHandler(p) { } 
       // 메뉴선택시 처리
       virtual void Command()
       {
             if( pMenuHandler != 0 ) pMenuHandler->MenuCommand( id );
       }
};
//------------------------------------------------------
class TapeList : public IMenuHandler
{
public:
       virtual bool Resolve( int id )
       {
             switch( id )
             {
             case 1: foo(); getch(); return true;
             case 2: foo(); getch(); return true;
             }
             return false;
       }
       void foo()
       {
             cout << "TapeList::foo" << endl;
       }
};
//-----------------------------------------------------
class VideoShop : public IMenuHandler
{
       TapeList tp_list;
public:
       void Init()
       {
             PopupMenu* p1 = new PopupMenu("ROOT");
             PopupMenu* p2 = new PopupMenu("고객관리");
             PopupMenu* p3 = new PopupMenu("Tape관리");
             PopupMenu* p4 = new PopupMenu("기타");
             p1->Add( p2 );
             p1->Add( p3 );
             p1->Add( p4 );
             p2->Add( new MenuItem("신규고객등록", 1, this) );
             p2->Add( new MenuItem("고객삭제", 2, this) );
             p2->Add( new MenuItem("기타", 3, this) );
             p3->Add( new MenuItem("신규Tape등록", 4, this) );
             p3->Add( new MenuItem("Tape삭제", 5, this) );
             p3->Add( new MenuItem("기타", 6, this) );
             // 메뉴를 처리하고 싶은 다음 객체를 등록시킨다.
             this->SetNext( &tp_list );
             p1->Command();
       }
       void AddCustomer()
       {
             cout << "신규고객등록처리" << endl;
       }
       virtual void MenuCommand( int id )
       {
             // 메뉴ID를 가지고 각 함수로 라우팅한다.
             if ( id == 1 ) AddCustomer();
             cout << "다른ID처리" << endl;
             getch();
       }
       virtual bool Resolve( int id )
       {
             if ( id == 1 )
             {
                    AddCustomer();
                    return true;
             }
             return false;
       }
};
int main()
{
       VideoShop v;
       v.Init();
}