using AGXUnity;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;


/// <summary>
/// This class allows for setting surface velocity of four individual conveyor belt "lanes".
/// The friction between cans and the conveyor belt as well as the friction between can and the side part of the conveyor belt.
/// </summary>
public class ConveyorBeltSystem : ScriptComponent
{
  // Control the speed of the conveyor belt lanes
  [SerializeField, Range(0, 2)] float speed1;
  [SerializeField, Range(0, 2)] float speed2;
  [SerializeField, Range(0, 2)] float speed3;

  public float[] Speed;

  protected List<AGXUnity.Collide.Shape> m_conveyorBelts;

  // Set the friction between can and the conveyor belts
  [SerializeField, Range(0, 1)] float ConveyorCanFrictionCoefficient;

  // Set the friction between can and the side of the conveyor
  [SerializeField, Range(0, 1)] float ConveyorSideCanFrictionCoefficient;

  private AGXUnity.ContactMaterial m_ConveyorCanContactMaterial;
  private AGXUnity.ContactMaterial m_ConveyorSideCanContactMaterial;

  Renderer[] m_conveyorRenderers;

  ContactMaterial GetContactMaterial( string name )
  {
    var materials = AGXUnity.ContactMaterialManager.Instance.ContactMaterials.Where(material => material.name == name);
    return materials.FirstOrDefault();
  }

  override protected void OnAwake()
  {
    // Will contain the renderers for each conveyorbelt.
    // We need that to be able to animate the textures
    m_conveyorRenderers = new Renderer[ 3 ];

    ContactMaterialManager.Instance.GetInitialized<ContactMaterialManager>();

    // Get contact material between Conveyor and Can
    string cmName = "ConveyorCanContactMaterial";
    m_ConveyorCanContactMaterial = GetContactMaterial( cmName );

    Debug.Assert( m_ConveyorCanContactMaterial != null );
    m_ConveyorCanContactMaterial.FrictionCoefficients = new Vector2( ConveyorCanFrictionCoefficient, ConveyorCanFrictionCoefficient );


    // Get contact material between ConveyorSide and Can
    cmName = "ConveyorSideCanContactMaterial";
    m_ConveyorSideCanContactMaterial = GetContactMaterial( cmName );
    Debug.Assert( m_ConveyorSideCanContactMaterial != null );

    m_ConveyorSideCanContactMaterial.FrictionCoefficients = new Vector2( ConveyorSideCanFrictionCoefficient, ConveyorSideCanFrictionCoefficient );

    // Find all shapes in this game object
    var shapes = GetComponentsInChildren<AGXUnity.Collide.Shape>();
    m_conveyorBelts = new List<AGXUnity.Collide.Shape>();

    // Store the ones with a specific name that corresponds to conveyor belts (defined in the cad model)
    foreach ( var s in shapes )
      if ( s.name.Contains( "Conveyor_" ) ) {
        m_conveyorBelts.Add( s );
      }

    // Sort by name
    m_conveyorBelts.Sort( delegate ( AGXUnity.Collide.Shape x, AGXUnity.Collide.Shape y )
    {
      if ( x.name == null && y.name == null ) return 0;
      else if ( x.name == null ) return -1;
      else if ( y.name == null ) return 1;
      else return x.name.CompareTo( y.name );
    } );

    // Get the renderer for each conveyor
    for ( int i = 0; i < m_conveyorBelts.Count; i++ )
      m_conveyorRenderers[ i ] = m_conveyorBelts[ i ].GetComponentInChildren<Renderer>();

  }
  protected override bool Initialize()
  {
    Speed = new float[ 3 ] { speed1, speed2, speed3 };

    return true;
  }

  // Update is called once per frame
  void Update()
  {
    // update the friction coefficients
    m_ConveyorCanContactMaterial.FrictionCoefficients = new Vector2( ConveyorCanFrictionCoefficient, ConveyorCanFrictionCoefficient );
    m_ConveyorSideCanContactMaterial.FrictionCoefficients = new Vector2( ConveyorSideCanFrictionCoefficient, ConveyorSideCanFrictionCoefficient );

    // Update the public array of conveyor speeds (used otherwise)
    Speed = new float[ 3 ] { speed1, speed2, speed3 };

    // Set the speed of the conveyor belts (surface velocity)
    int i = 0;
    foreach ( var geometry in m_conveyorBelts ) {
      var speed = Speed[i];

      // Animate the texture
      m_conveyorRenderers[ i ].material.SetVector( "_Offset", Time.time*new Vector2( speed, 0 ) );

      // Update the surface velocity local to the geometry
      var velocity = new agx.Vec3(speed, 0, 0);
      geometry.NativeGeometry.setSurfaceVelocity( new agx.Vec3f( (float)velocity[ 0 ], (float)velocity[ 1 ], (float)velocity[ 2 ] ) );
      i++;
    }
  }
}

