import 'package:comunic/helpers/account_helper.dart';
import 'package:comunic/models/conversation.dart';
import 'package:comunic/models/conversation_message.dart';
import 'package:comunic/ui/routes/conversation_message_stats_route.dart';
import 'package:comunic/ui/routes/conversation_route.dart';
import 'package:comunic/ui/routes/main_route/page_info.dart';
import 'package:comunic/ui/routes/settings/account_settings_route.dart';
import 'package:comunic/ui/screens/call_screen.dart';
import 'package:comunic/ui/screens/conversations_list_screen.dart';
import 'package:comunic/ui/screens/friends_list_screen.dart';
import 'package:comunic/ui/screens/group_screen.dart';
import 'package:comunic/ui/screens/groups_list_screen.dart';
import 'package:comunic/ui/screens/newest_posts.dart';
import 'package:comunic/ui/screens/notifications_screen.dart';
import 'package:comunic/ui/screens/other_friends_lists_screen.dart';
import 'package:comunic/ui/screens/search_screen.dart';
import 'package:comunic/ui/screens/user_access_denied_screen.dart';
import 'package:comunic/ui/screens/user_page_screen.dart';
import 'package:comunic/utils/account_utils.dart';
import 'package:comunic/utils/intl_utils.dart';
import 'package:comunic/utils/ui_utils.dart';
import 'package:flutter/material.dart';

/// Abstract main application route
///
/// This mixin contains methods available in all display modes
///
/// @author Pierre Hubert

/// Main controller key statically shared across application
///
/// Do not use it directly to avoid context leak, instead use the access method
/// [MainController.of]
final mainControllerKey = GlobalKey<MainController>();

mixin MainRoute implements StatefulWidget {}

/// Public interface of home controller
abstract class MainController extends State<MainRoute> {
  final _pagesStack = <PageInfo>[];

  /// Default page of the application
  PageInfo get defaultPage;

  /// Get the current page being active in the application
  PageInfo get currentPage => _pagesStack.last;

  /// Get the current number of page in application stack
  int get numberOfPages => _pagesStack.length;

  /// Get current instance of Home controller
  static MainController of(BuildContext context) {
    assert(context != null); // A future implementation might need context again
    return mainControllerKey.currentState;
  }

  @override
  void initState() {
    super.initState();
    _pagesStack.add(defaultPage);
  }

  /// Push a new page
  void pushPage(PageInfo page) {
    popUntilMainRoute();
    setState(() => _pagesStack.add(page));
  }

  /// Pop current page. Do not call this method directly.
  @protected
  void doPopPage() {
    if (_pagesStack.length > 0) setState(() => _pagesStack.removeLast());
  }

  /// Pop until main route is reached
  void popUntilMainRoute() => Navigator.of(context).popUntil((settings) =>
      ModalRoute.of(context).isCurrent || !ModalRoute.of(context).isActive);

  /// Go to the previous page (use for [WillPop] widget)
  @protected
  Future<bool> willPop() async {
    if (numberOfPages == 1) return true;

    popPage();
    return false;
  }

  /// Open notifications page
  void openNotificationsPage() => pushPage(PageInfo(
      type: PageType.NOTIFICATIONS_PAGE, child: NotificationsScreen()));

  /// Open conversations page
  void openConversationsPage() => pushPage(PageInfo(
      type: PageType.CONVERSATIONS_LIST_PAGE,
      child: ConversationsListScreen()));

  /// Open latest posts page
  void openLatestPostsPage() => pushPage(
      PageInfo(type: PageType.LATEST_POSTS_PAGE, child: NewestPostsScreen()));

  /// Open user page
  void openUserPage(int userID) => pushPage(PageInfo(
      type: PageType.USER_PAGE,
      child: UserPageScreen(userID: userID),
      id: userID));

  void openUserAccessDeniedPage(int userID) =>
      pushPage(PageInfo(child: UserAccessDeniedScreen(userID: userID)));

  /// Open current user page
  void openCurrentUserPage() => this.openUserPage(userID());

  /// Open a specific group page specified by its [groupID]
  void openGroup(int groupID) => pushPage(PageInfo(
      type: PageType.GROUP_PAGE,
      child: GroupPageScreen(groupID: groupID),
      id: groupID));

  /// Display the list of friends of current user
  void openFriendsList() => pushPage(PageInfo(
        type: PageType.FRIENDS_LIST_PAGE,
        child: FriendsListScreen(),
        canShowAsDialog: true,
      ));

  /// Open search page
  void openSearchPage() => pushPage(PageInfo(child: SearchScreen()));

  /// Open groups list page
  void openGroupsListPage() => pushPage(PageInfo(child: GroupsListScreen()));

  /// Display the list of friends of a user
  void openUserFriendsList(int id) {
    if (id != userID())
      pushPage(PageInfo(
        child: OtherUserFriendsListScreen(userID: id),
        canShowAsDialog: true,
      ));
    else
      openFriendsList();
  }

  /// Push a new widget
  void push(Widget w,
          {bool hideNavBar = false, bool canShowAsDialog = false}) =>
      pushPage(PageInfo(
          child: w, hideNavBar: hideNavBar, canShowAsDialog: canShowAsDialog));

  /// Open a conversation
  void openConversation(int convID, {fullScreen: false}) => pushPage(PageInfo(
        type: PageType.CONVERSATION_PAGE,
        id: convID,
        child: ConversationRoute(conversationID: convID),
        hideNavBar: true,
      ));

  /// Open a conversation message statistics page
  void openConversationMessageStats(
          Conversation conv, ConversationMessage message) =>
      pushPage(PageInfo(
        child: ConversationMessageStatsRoute(
          conv: conv,
          message: message,
        ),
        hideNavBar: true,
        canShowAsDialog: true,
      ));

  /// Start a call for a given conversation
  void startCall(int convID) =>
      pushPage(PageInfo(child: CallScreen(convID: convID), hideNavBar: true));

  /// Open settings
  void openSettings() => Navigator.of(context)
      .push(MaterialPageRoute(builder: (c) => AccountSettingsRoute()));

  /// Handle logout requests
  void requestLogout() async {
    if (!await showConfirmDialog(
        context: context,
        message: tr("Do you really want to sign out from the application ?"),
        title: tr("Sign out"))) return;

    popUntilMainRoute();

    await AccountHelper().signOut();
  }

  /// Pop current page. Last page can not be popped
  ///
  /// If the current route is not the main route, we pop one page
  void popPage() {
    if (!ModalRoute.of(context).isCurrent)
      Navigator.of(context).pop();
    else
      doPopPage();
  }
}