/*
 * The MIT License (MIT)
 * 
 * Copyright (c) 2019 Alibaba Group
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
import 'dart:async';
import 'dart:io';

import 'package:flutter/material.dart';
import 'package:flutter/semantics.dart';
import 'package:flutter/services.dart';
import '../channel/boost_channel.dart';
import 'boost_container.dart';
import '../flutter_boost.dart';
import '../logger.dart';

class ContainerCoordinator {
  static ContainerCoordinator get singleton => _instance;

  static ContainerCoordinator _instance;

  final Map<String, PageBuilder> _pageBuilders = <String, PageBuilder>{};
  PageBuilder _defaultPageBuilder;

  ContainerCoordinator(BoostChannel channel) {
    assert(_instance == null);

    _instance = this;

    channel.addEventListener("lifecycle",
        (String name, Map arguments) => _onChannelEvent(arguments));

    channel.addMethodHandler((MethodCall call) => _onMethodCall(call));
  }

  BoostContainerSettings _createContainerSettings(
      String name, Map params, String pageId) {
    Widget page;

    final BoostContainerSettings routeSettings = BoostContainerSettings(
        uniqueId: pageId,
        name: name,
        params: params,
        builder: (BuildContext ctx) {
          //Try to build a page using keyed builder.
          if (_pageBuilders[name] != null) {
            page = _pageBuilders[name](name, params, pageId);
          }

          //Build a page using default builder.
          if (page == null && _defaultPageBuilder != null) {
            page = _defaultPageBuilder(name, params, pageId);
          }

          assert(page != null);
          Logger.log('build widget:$page for page:$name($pageId)');

          return page;
        });

    return routeSettings;
  }

  //Register a default page builder.
  void registerDefaultPageBuilder(PageBuilder builder) {
    _defaultPageBuilder = builder;
  }

  //Register page builder for a key.
  void registerPageBuilder(String pageName, PageBuilder builder) {
    if (pageName != null && builder != null) {
      _pageBuilders[pageName] = builder;
    }
  }

  void registerPageBuilders(Map<String, PageBuilder> builders) {
    if (builders?.isNotEmpty == true) {
      _pageBuilders.addAll(builders);
    }
  }

  Future<dynamic> _onChannelEvent(dynamic event) {
    if (event is Map) {
      Map map = event;
      final String type = map['type'];

      Logger.log("onEvent $type");

      switch (type) {
        //Handler back key pressed event.
        case 'backPressedCallback':
          {
            final String id = map['uniqueId'];
            FlutterBoost.containerManager
                ?.containerStateOf(id)
                ?.performBackPressed();
          }
          break;
        //Enter foregroud
        case 'foreground':
          {
            FlutterBoost.containerManager?.setForeground();
          }
          break;
        //Enter background
        case 'background':
          {
            FlutterBoost.containerManager?.setBackground();
          }
          break;
        //Schedule a frame.
        case 'scheduleFrame':
          {
            WidgetsBinding.instance.scheduleForcedFrame();
            Future<dynamic>.delayed(Duration(milliseconds: 250),
                () => WidgetsBinding.instance.scheduleFrame());
          }
          break;
      }
    }

    return Future<dynamic>(() {});
  }

  Future<dynamic> _onMethodCall(MethodCall call) {
    Logger.log("onMetohdCall ${call.method}");

    switch (call.method) {
      case "didInitPageContainer":
        {
          String pageName = call.arguments["pageName"];
          Map params = call.arguments["params"];
          String uniqueId = call.arguments["uniqueId"];
          _nativeContainerDidInit(pageName, params, uniqueId);
        }
        break;
      case "willShowPageContainer":
        {
          String pageName = call.arguments["pageName"];
          Map params = call.arguments["params"];
          String uniqueId = call.arguments["uniqueId"];
          _nativeContainerWillShow(pageName, params, uniqueId);
        }
        break;
      case "didShowPageContainer":
        {
          String pageName = call.arguments["pageName"];
          Map params = call.arguments["params"];
          String uniqueId = call.arguments["uniqueId"];
          nativeContainerDidShow(pageName, params, uniqueId);
        }
        break;
      case "willDisappearPageContainer":
        {
          String pageName = call.arguments["pageName"];
          Map params = call.arguments["params"];
          String uniqueId = call.arguments["uniqueId"];
          _nativeContainerWillDisappear(pageName, params, uniqueId);
        }
        break;
      case "didDisappearPageContainer":
        {
          String pageName = call.arguments["pageName"];
          Map params = call.arguments["params"];
          String uniqueId = call.arguments["uniqueId"];
          _nativeContainerDidDisappear(pageName, params, uniqueId);
        }
        break;
      case "willDeallocPageContainer":
        {
          String pageName = call.arguments["pageName"];
          Map params = call.arguments["params"];
          String uniqueId = call.arguments["uniqueId"];
          _nativeContainerWillDealloc(pageName, params, uniqueId);
        }
        break;
      case "onNativePageResult":
        {}
        break;
    }

    return Future<dynamic>(() {});
  }

  bool _nativeContainerWillShow(String name, Map params, String pageId) {
    if (FlutterBoost.containerManager?.containsContainer(pageId) != true) {
      FlutterBoost.containerManager
          ?.pushContainer(_createContainerSettings(name, params, pageId));
    }

    return true;
  }

  bool nativeContainerDidShow(String name, Map params, String pageId) {
    FlutterBoost.containerManager
        ?.showContainer(_createContainerSettings(name, params, pageId));

    //在Android上对无障碍辅助模式的兼容
    if (Platform.isAndroid) {
      try {
        final SemanticsOwner owner =
            WidgetsBinding.instance.pipelineOwner?.semanticsOwner;
        final SemanticsNode root = owner?.rootSemanticsNode;
        root?.detach();
        root?.attach(owner);
      } catch (e) {
        assert(false, e.toString());
      }
    }

    performContainerLifeCycle(_createContainerSettings(name, params, pageId),
        ContainerLifeCycle.Appear);

    Logger.log(
        'native containner did show,\nmanager dump:\n${FlutterBoost.containerManager?.dump()}');

    return true;
  }

  bool _nativeContainerWillDisappear(String name, Map params, String pageId) {
    performContainerLifeCycle(_createContainerSettings(name, params, pageId),
        ContainerLifeCycle.WillDisappear);
    return true;
  }

  bool _nativeContainerDidDisappear(String name, Map params, String pageId) {
    performContainerLifeCycle(_createContainerSettings(name, params, pageId),
        ContainerLifeCycle.Disappear);
    return true;
  }

  bool _nativeContainerDidInit(String name, Map params, String pageId) {
    performContainerLifeCycle(_createContainerSettings(name, params, pageId),
        ContainerLifeCycle.Init);
    return true;
  }

  bool _nativeContainerWillDealloc(String name, Map params, String pageId) {
    try{

      performContainerLifeCycle(_createContainerSettings(name, params, pageId),
          ContainerLifeCycle.Destroy);
    } catch (e){
      Logger.log(
          'nativeContainerWillDealloc error: ${e}' );
    }

    FlutterBoost.containerManager?.remove(pageId);

    Logger.log(
        'native containner dealloc, \n manager dump:\n${FlutterBoost.containerManager?.dump()}');

    return true;
  }

  static void performContainerLifeCycle(
      BoostContainerSettings settings, ContainerLifeCycle lifeCycle) {
    for (BoostContainerLifeCycleObserver observer in FlutterBoost
        .singleton.observersHolder
        .observersOf<BoostContainerLifeCycleObserver>()) {
      observer(lifeCycle, settings);
    }

    Logger.log(
        'BoostContainerLifeCycleObserver container:${settings.name} lifeCycle:$lifeCycle');
  }
}