import Observable from "../../helper/observable";
import { h, Component } from "preact";

export default abstract class Modal<T> {
   // Static
   private static modalObservableServer = new Observable<{ modal: Modal<any>, close: boolean }>(false);
   public static modalObservable = Modal.modalObservableServer.getPublicApi();


   protected abstract title: string;

   // Private
   private onResult: (result: T | null) => void;

   // Protected
   protected result(value: T | null) {
      if (this.onResult)
         this.onResult(value);
   }

   //Public
   /**
    * This function shows the modal
    * Do not cell when using getResult()
    */
   public async show() {
      Modal.modalObservableServer.send({ modal: this, close: false });
   }

   /**
    * Shows the modal and waits for result.
    * 
    * Call close when successful
    */
   public async getResult() {
      this.show();
      return new Promise<T | null>((yes) => this.onResult = yes);
   }

   public close() {
      Modal.modalObservableServer.send({ modal: this, close: true });
   }

   public abstract getComponent(): JSX.Element;

   public static BaseModal = class BaseModal<T> extends Component<{ modal: Modal<T> }, {}> {
      render() {
         return <div class="modal_container" onClick={(evt) => {
            let path = evt.composedPath();
            if (!path.find(e => {
               let res = false;
               let s = (e as Element);
               if (s) {
                  if (s.classList) {
                     res = s.classList.contains("card")
                  }
               }
               return res;
            })) {
               this.props.modal.result(null)
            }
         }} onKeyDown={evt => {
            if (evt.keyCode === 27) {
               this.props.modal.result(null)
            }
         }}>
            <div class="card" >
               <h3 class="section">{this.props.modal.title}</h3>
               {this.props.children}
            </div>
         </div>
      }
   }

}


// export default abstract class Modal<T, S> extends Component<{}, S> {


//    // Abstract
//    protected abstract renderChilds(): JSX.Element;
//    protected abstract title: string;



//    render() {
//       return <div class="modal_container" onClick={(evt) => {
//          let path = evt.composedPath();
//          if (!path.find(e => {
//             let res = false;
//             let s = (e as Element);
//             if (s) {
//                if (s.classList) {
//                   res = s.classList.contains("card")
//                }
//             }
//             return res;
//          })) {
//             this.result(null)
//          }
//       }} onKeyDown={evt => {
//          if (evt.keyCode === 27) {
//             this.result(null)
//          }
//       }}>
//          <div class="card" >
//             <h3 class="section">{this.title}</h3>
//             {this.renderChilds()}
//          </div>
//       </div>
//    }
// }

export class ModalComponent extends Component<{}, { modal: Modal<any> | undefined, component: JSX.Element | undefined }>{
   constructor(props) {
      super(props);
      this.onModal = this.onModal.bind(this);
   }

   onModal([{ modal, close }]: { modal: Modal<any>, close: boolean }[]) {
      if (!close && this.state.modal !== modal) {
         this.setState({ modal: modal, component: modal.getComponent() })
      }
      else {
         if (this.state.modal === modal) // Only close if the same
            this.setState({ modal: undefined, component: undefined })
      }
   }

   componentWillMount() {
      Modal.modalObservable.subscribe(this.onModal, true);
   }

   componentWillUnmount() {
      Modal.modalObservable.unsubscribe(this.onModal);
   }

   render() {
      return <div>
         {this.state.component}
      </div>
   }
}