import { cloneDeep } from "lodash";
import { pond } from "protobuf-ts/pond";
import { or } from "utils/types";
import area from "@turf/area";
import { Feature } from "geojson";
import { GeometryMapping } from "./GeometryMapping";
import GrainDescriber from "grain/GrainDescriber";

export class Field {
  public settings: pond.FieldSettings = pond.FieldSettings.create();
  public status: pond.FieldStatus = pond.FieldStatus.create();
  public permissions: pond.Permission[] = [];

  public static create(pf?: pond.Field): Field {
    let my = new Field();
    if (pf) {
      my.settings = pond.FieldSettings.fromObject(cloneDeep(or(pf.settings, {})));
      my.status = pond.FieldStatus.fromObject(cloneDeep(or(pf.status, {})));
      my.permissions = pf.fieldPermissions;
    }
    return my;
  }

  public static clone(other?: Field): Field {
    if (other) {
      return Field.create(
        pond.Field.fromObject({
          settings: cloneDeep(other.settings),
          status: cloneDeep(other.status),
          permissions: cloneDeep(other.permissions)
        })
      );
    }
    return Field.create();
  }

  public calculateAcres(): number {
    let totalAcres = 0;
    const metersToAcresConversionValue = 0.00024710538146717;
    let geoData = this.settings.fieldGeoData;
    if (geoData) {
      //round to 2 decimal places
      totalAcres =
        Math.round(
          area(
            GeometryMapping.geoJSON(geoData.geoShape, geoData.shapes, geoData.holes) as Feature
          ) *
            metersToAcresConversionValue *
            100
        ) / 100;
    }
    return totalAcres;
  }

  public static any(data: any): Field {
    return Field.create(pond.Field.fromObject(cloneDeep(data)));
  }

  public key(): string {
    return this.settings.key;
  }

  public fieldName(): string {
    return this.settings.fieldName;
  }

  public landLoc(): string {
    return this.settings.landLocation;
  }

  public crop(): pond.Grain {
    return this.settings.crop;
  }

  public name(): string {
    return this.settings.fieldName;
  }

  public objectType(): pond.ObjectType {
    return pond.ObjectType.OBJECT_TYPE_FIELD;
  }

  public objectTypeString(): string {
    return "Field";
  }

  public grain(): pond.Grain {
    return this.settings.crop;
  }

  public subtype(): string {
    return this.settings.grainSubtype;
  }

  public customType(): string {
    let c = "";
    if (this.settings) {
      c = this.settings.customGrain;
    }
    return c;
  }

  public acres(): number {
    if (this.settings.acres > 0) {
      return this.settings.acres;
    } else {
      return this.calculateAcres();
    }
  }

  //these are for when using the field as a source for a grain transaction to compare if the destination matches
  public storage(): pond.BinStorage {
    let storage = pond.BinStorage.BIN_STORAGE_SUPPORTED_GRAIN;
    if (this.settings.crop === pond.Grain.GRAIN_CUSTOM) {
      storage = pond.BinStorage.BIN_STORAGE_UNSUPPORTED_GRAIN;
    }
    return storage;
  }

  /**
   * returns the bushels per tonne set in the bins settings, if not set will return 1
   * @returns 1 or bushels per tonne
   */
  public bushelsPerTonne(): number {
    //trying to avoid a divide by 0 error by only returning a value greater than 0
    //since to get the weight you divide the current bushels by the bushels per tonne
    let bpt = 1;
    if (this.settings.bushelsPerTonne > 0) {
      bpt = this.settings.bushelsPerTonne;
    }
    return bpt;
  }

  public center(): pond.Coordinates {
    let coords = pond.Coordinates.create();
    if (this.settings.fieldGeoData) {
      let minLong = 0;
      let minLat = 0;
      let maxLong = 0;
      let maxLat = 0;
      this.settings.fieldGeoData.shapes.forEach(shape => {
        shape.points.forEach(pair => {
          if (pair.longitude < minLong || minLong === 0) minLong = pair.longitude;
          if (pair.longitude > maxLong || maxLong === 0) maxLong = pair.longitude;
          if (pair.latitude < minLat || minLat === 0) minLat = pair.latitude;
          if (pair.latitude > maxLat || maxLat === 0) maxLat = pair.latitude;
        });
      });
      coords.longitude = (maxLong + minLong) / 2;
      coords.latitude = (maxLat + minLat) / 2;
    }
    return coords;
  }

  public grainName(): string {
    if (this.grain() !== pond.Grain.GRAIN_INVALID) {
      if (this.grain() === pond.Grain.GRAIN_CUSTOM) {
        return this.customType();
      } else {
        return GrainDescriber(this.grain()).name;
      }
    } else {
      return "None";
    }
  }
}
