
import 'dart:convert';
import 'dart:io';

import 'package:flutter/material.dart';
import 'package:path_provider/path_provider.dart';
import 'package:provider/provider.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:twong/providers/user.dart';

import 'package:twong/utils/index.dart';
import 'package:twong/models/index.dart';

class Account {
  String account;
  String password;
  Token token;

  String toString() {
    return '{"account": "$account", "password": "$password", "token": ${json.encode(token.toJson())}}';
  }

  static Account fromString(String str) {
    Account account = Account();
    try {
      var map = json.decode(str);
      account.account = map["account"];
      account.password = map["password"];
      account.token = Token.fromJson(map["token"]);
    } catch (err) {
      Log.e(err);
    }
    return account;
  }
}

class SaveKey {
  static const String first = 'first';
  static const String account = 'account';
  static const String home = 'home';
  static const String balance = 'balance';
  static const String areas = 'areas';
}

class Cache {
  static UserInfo _user;
  static Account _account;
  static bool checked = false;
  static SharedPreferences _pre;
  static Map<String, dynamic> _cacheData;
  static GlobalKey<NavigatorState> _navigatorKey;
  static GlobalKey<NavigatorState> get navigator => _navigatorKey;
  static BuildContext get context => _navigatorKey?.currentState?.overlay?.context;

  static void setKey(GlobalKey<NavigatorState> key) {
    _navigatorKey = key;
  }

  static Future initial () async {
    _cacheData = new Map<String, dynamic>();
    _pre = await SharedPreferences.getInstance();

    var str = loadString(SaveKey.account);
    if (str != null) {
      _account = Account.fromString(str);
    } else {
      _account = Account();
    }

    print('cache init over');
  }

  static bool get isVip  => user != null && user.vip_level > 1;

  static saveString(String key, String value) {
    _pre.setString(key, value);
  }

  static saveBool(String key, bool value) {
    _pre.setBool(key, value);
  }

  static saveInt(String key, int value) {
    _pre.setInt(key, value);
  }

  static saveStringList(String key, List<String> value) {
    _pre.setStringList(key, value);
  }

  static delKey(String key) {
    _pre.remove(key);
  }

  static loadInt(String key) => _pre.getInt(key);
  static loadBool(String key) => _pre.getBool(key);
  static loadString(String key) => _pre.getString(key);
  static loadStringList(String key) => _pre.getStringList(key);

  static updateAccount(String account, String password) {
    _account.account = account;
    _account.password = password;
    if(_account.account == null || _account.password == null) {
      delKey(SaveKey.account);
    } else {
      saveString(SaveKey.account, _account.toString());
    }
  }

  static Token get token {
    if(_account.token?.token == null) return null;
    var token = _account.token;
    var time = DateTime.parse(token.expires_time);
    if(time.isAfter(DateTime.now())) return token;
    else return null;
  }

  static Account get account => _account;

  static void updateToken(Token token) {
    _account.token = token == null ? Token() : token;
    saveString(SaveKey.account, _account.toString());
    Request.updateToken();
  }

  static UserInfo get user {
    return _user;
  }

  static void updateUserInfo(UserInfo user) {
    _user = user;
    Provider.of<UserModel>(context, listen: false).update(user);
  }

  static void updateUser(User user) {
    _user.update(user);
  }

  static void set(String key, dynamic value) {
    _cacheData[key] = value;
  }

  static T get<T>(String key) {
    if(!_cacheData.containsKey(key)) return null;
    return _cacheData[key] as T;
  }

  static void clearCache() {
    _cacheData.clear();
  }

  static void clearSaved() {
    _pre.clear();
  }

  static void clearAll() {
    updateToken(null);
    updateUserInfo(null);
    updateAccount(null, null);
    clearCache();
    Log.i("clear all data");
  }

  static Future<String> loadCache() async {
    Directory tempDir = await getTemporaryDirectory();
    double value = await _getDirSize(tempDir);
    // tempDir.list(followLinks: false, recursive: true).listen((file) {
    //   print(file.path);
    // });
    return _renderSize(value);
  }

  static _renderSize(double value) {
    if (null == value) {
      return 0;
    }
    List<String> unitArr = List()
      ..add('B')
      ..add('K')
      ..add('M')
      ..add('G');
    int index = 0;
    while (value > 1024) {
      index++;
      value = value / 1024;
    }
    String size = value.toStringAsFixed(2);
    return size + unitArr[index];
  }

  static Future<double> _getDirSize(FileSystemEntity file) async {
    if (file is File) {
      int length = await file.length();
      return double.parse(length.toString());
    }
    if (file is Directory) {
      final List<FileSystemEntity> children = file.listSync();
      double total = 0;
      if (children != null)
        for (final FileSystemEntity child in children)
          total += await _getDirSize(child);
      return total;
    }
    return 0;
  }

  static Future<String> clearFiles() async {
    Directory tempDir = await getTemporaryDirectory();
    await delDir(tempDir);
    return loadCache();
  }

  static Future delDir(FileSystemEntity file) async {
    if (file is Directory) {
      final List<FileSystemEntity> children = file.listSync();
      for (final FileSystemEntity child in children) {
        await delDir(child);
      }
    } else {
      await file.delete();
    }
  }
}

extension VersionExtension on Version {
  bool operator < (Version b) {
    var strA = version.split('.');
    var strB = b.version.split('.');

    if(int.parse(strA[0]) < int.parse(strB[0])) return true;
    else if(strA[0] == strB[0]) {
      if (int.parse(strA[1]) < int.parse(strB[1])) return true;
      else if(strA[1] == strB[1]) {
        if (int.parse(strA[2]) < int.parse(strB[2])) return true;
      }
    }

    return false;
  }

  bool operator > (Version b) {
    var strA = version.split('.');
    var strB = b.version.split('.');

    if(int.parse(strA[0]) > int.parse(strB[0])) return true;
    else if(strA[0] == strB[0]) {
      if (int.parse(strA[1]) > int.parse(strB[1])) return true;
      else if(strA[1] == strB[1]) {
        if (int.parse(strA[2]) > int.parse(strB[2])) return true;
      }
    }

    return false;
  }
}