/**
 * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
 *
 * @author Alexander Rose <alexander.rose@weirdbyte.de>
 */

import { Model, ModelSymmetry } from '../../mol-model/structure';
import { StateTransformer } from '../../mol-state';
import { ModelUnitcell3D } from '../state/transforms/representation';
import { ShapeRepresentation } from '../../mol-repr/shape/representation';
import { Text } from '../../mol-geo/geometry/text/text';
import { Shape } from '../../mol-model/shape';
import { ColorNames } from '../../mol-util/color/names';
import { RuntimeContext } from '../../mol-task';
import { ParamDefinition as PD } from '../../mol-util/param-definition';
import { Mesh } from '../../mol-geo/geometry/mesh/mesh';
import { MeshBuilder } from '../../mol-geo/geometry/mesh/mesh-builder';
import { BoxCage } from '../../mol-geo/primitive/box';
import { Mat4, Vec3 } from '../../mol-math/linear-algebra';
import { transformCage, copyCage } from '../../mol-geo/primitive/cage';
import { radToDeg } from '../../mol-math/misc';

const translate05 = Mat4.fromTranslation(Mat4(), Vec3.create(0.5, 0.5, 0.5))
const unitCage = transformCage(copyCage(BoxCage()), translate05)

const tmpRef = Vec3()
const tmpTranslate = Mat4()

interface UnitcellData {
    symmetry: ModelSymmetry
    ref: Vec3
}

export const UnitcellParams = {
    ...Mesh.Params,
    cellColor: PD.Color(ColorNames.orange),
    cellScale: PD.Numeric(2, { min: 0.1, max: 5, step: 0.1 })
}
export type UnitcellParams = typeof UnitcellParams
export type UnitcellProps = PD.Values<UnitcellParams>

function getUnitcellMesh(data: UnitcellData, props: UnitcellProps, mesh?: Mesh) {
    const state = MeshBuilder.createState(256, 128, mesh)

    const { fromFractional } = data.symmetry.spacegroup.cell

    Vec3.floor(tmpRef, data.ref)
    Mat4.fromTranslation(tmpTranslate, tmpRef)
    const cellCage = transformCage(copyCage(unitCage), tmpTranslate)

    const radius = (Math.cbrt(data.symmetry.spacegroup.cell.volume) / 300) * props.cellScale
    state.currentGroup = 1
    MeshBuilder.addCage(state, fromFractional, cellCage, radius, 2, 20)

    return MeshBuilder.getMesh(state)
}

export async function getUnitcellRepresentation(ctx: RuntimeContext, model: Model, params: StateTransformer.Params<ModelUnitcell3D>, prev?: ShapeRepresentation<UnitcellData, Text, Text.Params>) {
    const repr = prev || ShapeRepresentation(getUnitcellShape, Mesh.Utils);
    const data = {
        symmetry: model.symmetry,
        ref: Vec3.transformMat4(Vec3(), Model.getCenter(model), model.symmetry.spacegroup.cell.toFractional)
    }
    await repr.createOrUpdate(params, data).runInContext(ctx);
    return repr;
}

function getUnitcellLabel(data: UnitcellData) {
    const { cell, name } = data.symmetry.spacegroup
    const { index, size, anglesInRadians } = cell
    const a = size[0].toFixed(2)
    const b = size[1].toFixed(2)
    const c = size[2].toFixed(2)
    const alpha = radToDeg(anglesInRadians[0]).toFixed(2)
    const beta = radToDeg(anglesInRadians[1]).toFixed(2)
    const gamma = radToDeg(anglesInRadians[2]).toFixed(2)
    const label: string[] = []
    // name
    label.push(`${name} #${index + 1}`)
    // sizes
    label.push(`${a}\u00D7${b}\u00D7${c} \u212B`)
    // angles
    label.push(`\u03b1=${alpha}\u00B0 \u03b2=${beta}\u00B0 \u03b3=${gamma}\u00B0`)
    return label.join(' | ')
}

function getUnitcellShape(ctx: RuntimeContext, data: UnitcellData, props: UnitcellProps, shape?: Shape<Mesh>) {
    const geo = getUnitcellMesh(data, props, shape && shape.geometry);
    const label = getUnitcellLabel(data)
    return Shape.create('Unitcell', data, geo, () => props.cellColor, () => 1, () => label)
}