import { Socket } from 'socket.io';
import { Vector3 } from 'three';
import {
  CharacterPacket,
  FullStatePacket,
  PositionUpdatePacket,
} from '../../common/types/packet';
import { IcyNetUser } from '../../common/types/user';
import { DatabasePony } from '../types/database';
import { Persistence } from './persistence';

export class Player {
  public id!: number;
  public position = new Vector3();
  public rotation = new Vector3();
  public velocity = new Vector3();
  public angularVelocity = new Vector3();
  public animState = 0;
  public character: CharacterPacket = {};

  constructor(
    private socket: Socket,
    private db: Persistence,
    public user: IcyNetUser,
  ) {}

  async initialize() {
    this.socket.on('set.move', (packet: FullStatePacket) => {
      this.fromPacket(packet);
    });

    this.socket.on('set.character', (info: CharacterPacket) => {
      this.setCharacter(info);

      this.socket.broadcast.emit('player.character', {
        id: this.user.id,
        ...this.getCharacter(),
      });
    });

    const user = await this.db.upsertUser(this.user);
    const pony = await this.db.getUserPony(user);

    if (pony) {
      this.fromSave(pony);
    } else {
      await this.db.upsertPony(user, this.toSave());
    }

    this.socket.emit('set.me', {
      ...this.getPublicUserProfile(),
      ...this.toSave(),
    });

    this.socket.broadcast.emit('player.join', this.getPublicUserProfile());

    if (pony && pony.character) {
      this.socket.broadcast.emit('player.character', {
        id: this.user.id,
        character: this.getCharacter(),
      });
    }
  }

  async dispose() {
    await this.db.upsertPony(this.user, this.toSave());
  }

  update(dt: number) {}

  fromPacket(packet: FullStatePacket | PositionUpdatePacket) {
    if ((packet as FullStatePacket).angular) {
      this.angularVelocity.fromArray((packet as FullStatePacket).angular!);
    }

    if ((packet as FullStatePacket).velocity) {
      this.velocity.fromArray((packet as FullStatePacket).velocity!);
    }

    if (packet.position) {
      this.position.fromArray(packet.position);
    }

    if (packet.rotation) {
      this.rotation.fromArray(packet.rotation as number[]);
    }

    if (packet.animState !== undefined) {
      this.animState = packet.animState;
    }
  }

  fromSave(save: DatabasePony) {
    save.position && this.position.fromArray(save.position as number[]);
    save.rotation && this.rotation.fromArray(save.rotation as number[]);
    save.character && this.setCharacter(save.character as CharacterPacket);
  }

  toSave(): DatabasePony {
    return {
      ...this.toUpdatePacket(),
      character: this.getCharacter(),
    };
  }

  toUpdatePacket(): PositionUpdatePacket {
    return {
      id: this.user.id,
      position: this.position.toArray(),
      rotation: this.rotation.toArray(),
      animState: this.animState,
    };
  }

  getPublicUserProfile(): Partial<IcyNetUser> {
    return {
      id: this.user.id,
      username: this.user.username,
      display_name: this.user.display_name,
    };
  }

  setCharacter(packet: CharacterPacket) {
    this.character = { ...this.character, ...packet };
  }

  getCharacter(): CharacterPacket {
    return this.character;
  }
}
