/*
 *   Famedly Matrix SDK
 *   Copyright (C) 2019, 2020 Famedly GmbH
 *
 *   This program is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU Affero General Public License as
 *   published by the Free Software Foundation, either version 3 of the
 *   License, or (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 *   GNU Affero General Public License for more details.
 *
 *   You should have received a copy of the GNU Affero General Public License
 *   along with this program.  If not, see .
 */
import 'dart:async';
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:mime/mime.dart';
import 'package:moor/moor.dart';
import 'model/device.dart';
import 'model/event_context.dart';
import 'model/events_sync_update.dart';
import 'model/filter.dart';
import 'model/keys_query_response.dart';
import 'model/login_response.dart';
import 'model/login_types.dart';
import 'model/matrix_connection_exception.dart';
import 'model/matrix_event.dart';
import 'model/matrix_exception.dart';
import 'model/matrix_keys.dart';
import 'model/notifications_query_response.dart';
import 'model/one_time_keys_claim_response.dart';
import 'model/open_graph_data.dart';
import 'model/open_id_credentials.dart';
import 'model/presence_content.dart';
import 'model/profile.dart';
import 'model/public_rooms_response.dart';
import 'model/push_rule_set.dart';
import 'model/pusher.dart';
import 'model/request_token_response.dart';
import 'model/room_alias_informations.dart';
import 'model/room_keys_info.dart';
import 'model/room_keys_keys.dart';
import 'model/server_capabilities.dart';
import 'model/supported_protocol.dart';
import 'model/supported_versions.dart';
import 'model/sync_update.dart';
import 'model/tag.dart';
import 'model/third_party_identifier.dart';
import 'model/third_party_location.dart';
import 'model/third_party_user.dart';
import 'model/timeline_history_response.dart';
import 'model/turn_server_credentials.dart';
import 'model/upload_key_signatures_response.dart';
import 'model/user_search_result.dart';
import 'model/well_known_informations.dart';
import 'model/who_is_info.dart';
enum RequestType { GET, POST, PUT, DELETE }
enum IdServerUnbindResult { success, no_success }
enum ThirdPartyIdentifierMedium { email, msisdn }
enum Membership { join, invite, leave, ban }
enum Direction { b, f }
enum Visibility { public, private }
enum CreateRoomPreset { private_chat, public_chat, trusted_private_chat }
String describeEnum(Object enumEntry) {
  final description = enumEntry.toString();
  final indexOfDot = description.indexOf('.');
  assert(indexOfDot != -1 && indexOfDot < description.length - 1);
  return description.substring(indexOfDot + 1);
}
class MatrixApi {
  /// The homeserver this client is communicating with.
  Uri homeserver;
  /// This is the access token for the matrix client. When it is undefined, then
  /// the user needs to sign in first.
  String accessToken;
  /// Matrix synchronisation is done with https long polling. This needs a
  /// timeout which is usually 30 seconds.
  int syncTimeoutSec;
  http.Client httpClient = http.Client();
  bool get _testMode =>
      homeserver.toString() == 'https://fakeserver.notexisting';
  int _timeoutFactor = 1;
  MatrixApi({
    this.homeserver,
    this.accessToken,
    http.Client httpClient,
    this.syncTimeoutSec = 30,
  }) {
    if (httpClient != null) {
      this.httpClient = httpClient;
    }
  }
  /// Used for all Matrix json requests using the [c2s API](https://matrix.org/docs/spec/client_server/r0.6.0.html).
  ///
  /// Throws: TimeoutException, FormatException, MatrixException
  ///
  /// You must first set [this.homeserver] and for some endpoints also
  /// [this.accessToken] before you can use this! For example to send a
  /// message to a Matrix room with the id '!fjd823j:example.com' you call:
  /// ```
  /// final resp = await request(
  ///   RequestType.PUT,
  ///   '/r0/rooms/!fjd823j:example.com/send/m.room.message/$txnId',
  ///   data: {
  ///     'msgtype': 'm.text',
  ///     'body': 'hello'
  ///   }
  ///  );
  /// ```
  ///
  Future