import 'package:common_pub/model/distance.dart';
import 'package:common_pub/model/pace.dart';
import 'package:common_pub/model/position.dart';
import 'package:common_pub/service/controller.dart';
import 'package:common_pub/ui/map_view/map_view.dart';
import 'package:common_pub/ui/map_view/view_map_trace.dart';
import '../service/api.dart' as pb;
import 'package:fixnum/fixnum.dart';


typedef MapId = Int64;


class ActiveInfo{
  var id = 0;
  var name = '';
  var cpAllCount = 0;
  var userList = <UserInfo>[];
  final isHide = false.obs;

  UserInfo? getUserById(int id){
    for (final one in userList){
      if(one.gameInfo.userId==id){
        return one;
      }
    }

    return null;
  }

  Future<UserInfo> newUserInfo(pb.ToOrienteerInGameInfo info)async{
    final r =await pb.ApiService.to.stub.toUserInActionBasicQuery(pb.ToUserInActionBasicQueryRequest(
      actId: id,
      userId: info.userId
    ));

    return UserInfo()
      ..routeInfo = r.courseBaseInfo
      ..userInfo = r.baseInfo
      ..gameInfo = info
    ;
  }


}
extension ActiveInfoExt on pb.ToActionInfo{
  Future<ActiveInfo> into() async {
    final info = await pb.ApiService.to.stub.toActionBasicQuery(
        pb.IdRequest(id: Int64(actId)));

    final out = ActiveInfo()
      ..id = actId
      ..name = info.actName
      ..cpAllCount = info.totalControlNum;


    for(final one in userList){
      out.userList.add(await out.newUserInfo(one));
    }

    return out;
  }
}


class UserInfo{
  final isHide = false.obs;
  String get name => userInfo.name;
  String get routeName => routeInfo.courseName;
  Pace get pace => Pace.perKm( gameInfo.gpsInfo.pace.seconds);
  Duration get duration => DateTime.now().difference(gameInfo.gameSaveInfo.startAt.toModel());
  Distance get distance => Distance(m: gameInfo.gpsInfo.distance.toDouble());
  var gameInfo = pb.ToOrienteerInGameInfo();
  var routeInfo = pb.CourseBaseInfo();
  var userInfo = pb.OrienteerBaseInfo();
  var trace = <TracePoint>[].obs;


  Distance get nextDistance {
    final one = nextWant;
    if(one!= null){
      final p1 = Position(longitude: one.ciPosition.longitude, latitude: one.ciPosition.latitude);
      final p22 = gameInfo.gpsInfo.gameGpsInfos.lastOrNull;
      if(p22 != null){
        final p2 = Position(longitude: p22.longitude, latitude: p22.latitude);
        return p1.distance(p2);
      }
    }
    return const Distance(m: 1000);
  }

  pb.ToControlPoint? get nextWant{
    final id = gameInfo.gameSaveInfo.nextControlPoint.id;
    for (final one in routeInfo.controlPointSortedList){
      if(one.id==id){
        return one;
      }
    }
    return null;
  }

  Future<void> update(pb.ToOrienteerInGameInfo info)async{
    final map = MapWatchService.instance;


    gameInfo = info;
    final indexMap = <int, TracePoint>{};

    for (final one in trace){
      indexMap[one.ts.inMilliseconds]=one;
    }

    for(final one in info.gpsInfo.gameGpsInfos){
      final t = one.gpsTime.toModel();
      final ts = gameInfo.gameSaveInfo.startAt.toModel().difference(t);
      if(ts.inMilliseconds>0 && !indexMap.containsKey(ts.inMilliseconds)){
        final pos = one.toModel();
        trace.add(TracePoint()
            ..ts = ts
            ..position=pos
        );
      }
    }

    if(map!.plugMap.isInitFinish){
      for (final one in trace){
        if(one.onMap==Offset.zero){
          one.onMap = await map.plugMap.gameMap.worldToPixel(one.position);
        }
      }
    }
  }
}


class MapWatchService extends PlugController{
  static final Rx<MapWatchService?> _instance = Rx(null);
  static MapWatchService? get instance => _instance.value;

  static Future<void> setMapById(MapId id)async{
    final info = await pb.ApiService.to.stub.toMapDetailV2(pb.IdRequest()
      ..id = id);

    final thisInstance = MapWatchService(
        id: id)
      ..name = info.mapName
      ..plugMap.gameMap = info.zipImage.toGameMap() ;

    thisInstance.addPlugs([thisInstance.plugMap]);

    _instance.value = thisInstance;
    thisInstance.init();
  }

  MapWatchService({required this.id});

  final MapId id;
  String name= '';
  final plugMap = PlugMap();
  final activeList = <ActiveInfo>[].obs;
}