using UnityEngine;
using System.Collections;
using System.Collections.Generic;

// Połączona linia
public class Rope : MonoBehaviour
{

    // Używany prefabrykat segmentu liny
    public GameObject ropeSegmentPrefab;

    // Lista obiektów fragmentów liny
    List<GameObject> ropeSegments = new List<GameObject>();

    public bool isIncreasing { get; set; }
    public bool isDecreasing { get; set; }

    public Rigidbody2D connectedObject;

    // Maksymalna długość jaką powinien mieć jeden segment liny (o ile to konieczne)
    public float maxRopeSegmentLength = 1.0f;

    // Szybkość wydłużania nowej liny
    public float ropeSpeed = 4.0f;

    // Obiekt LineRenderer wyświetlający faktyczną linę
    LineRenderer lineRenderer;

    void Start()
    {

        // Zapisuje obiekt lineRenderer w zmiennej, tak by nie trzeba 
        // go było wyszukiwać w każdej ramce
        lineRenderer = GetComponent<LineRenderer>();

        // Zerujemy linę, tak by była gotowa do użycia
        ResetLength();

    }

    // Metoda usuwa wszystkie segmenty bieżącej liny i tworzy nową
    public void ResetLength()
    {

        foreach (GameObject segment in ropeSegments)
        {
            Destroy(segment);

        }

        ropeSegments = new List<GameObject>();

        isDecreasing = false;
        isIncreasing = false;

        CreateRopeSegment();

    }

    // Metoda dołącza nowy segment liny na jej wierzchołku
    void CreateRopeSegment()
    {

        //Tworzymy obiekt nowego segmentu liny
        GameObject segment = (GameObject)Instantiate(
            ropeSegmentPrefab,
            this.transform.position,
            Quaternion.identity);

        // Ustawiamy segment liny jako obiekt podrzędnego tego obiektu, 
        // z zachowaniem jego położenia względem świata
        segment.transform.SetParent(this.transform, true);

        // Pobieramy z segmentu obiekt Rigidbody
        Rigidbody2D segmentBody = segment.GetComponent<Rigidbody2D>();

        // Pobieramy połączenie z segmentu
        SpringJoint2D segmentJoint =
                segment.GetComponent<SpringJoint2D>();

        // Zgłaszamy błąd jeśli pefabrykat segmentu nie zwróci obiektu Rigidbody albo 
        // SpringJoint - oba te obiekty są nam potrzebne
        if (segmentBody == null || segmentJoint == null)
        {
            Debug.LogError("Prefabryka segmentu liny nie ma obiektu " +
                      "Rigidbody2D i SpringJoint2D!");
            return;
        }

        // Po sprawdzeniu warunku, należy go dodać do listy segmentów liny
        ropeSegments.Insert(0, segment);

        // Jeśli to jest PIERWSZY segment, to musi być połączony z krasnalem

        if (ropeSegments.Count == 1)
        {
            // Łączymy złącze połączonego obiektu z segmentem 
            SpringJoint2D connectedObjectJoint =
                      connectedObject.GetComponent<SpringJoint2D>();

            connectedObjectJoint.connectedBody = segmentBody;
            connectedObjectJoint.distance = 0.1f;

            // Ustawiamy to złącze tak, by miało maksymalną długość
            segmentJoint.distance = maxRopeSegmentLength;
        }
        else
        {
            // To jest dodatkowy segment liny. Musimy do niego dołączyć segment,
            // który dotychczas znajdował sie na samej górze liny

            // Pobieramy drugi segment
            GameObject nextSegment = ropeSegments[1];

            // Pobieramy złącze, które powinno być dołączone do tego segmentu
            SpringJoint2D nextSegmentJoint =
                      nextSegment.GetComponent<SpringJoint2D>();

            // Łączymy złącze z bieżącym elementem
            nextSegmentJoint.connectedBody = segmentBody;

            // Ustawiamy początkową odległość segmentu na 0 jednostek
            segmentJoint.distance = 0.0f;
        }

        // Łączymy nowy segment do punktu zaczepienia (kotwicy) liny (czyli tego obiektu)
        segmentJoint.connectedBody = this.GetComponent<Rigidbody2D>();
    }

    // Metoda jest wywoływana podczas skracania liny, kiedy trzeba usunąć jej segment
    void RemoveRopeSegment()
    {

        // Jeśli długość liny nie przekracza 2 segmentów, to kończymy
        if (ropeSegments.Count < 2)
        {
            return;
        }

        // Pobieramy górny segment oraz segment bezpośrednio pod nim
        GameObject topSegment = ropeSegments[0];
        GameObject nextSegment = ropeSegments[1];

        // Łączymy drugi segment do punktu zaczepienia (kotwicy) liny 
        SpringJoint2D nextSegmentJoint =
                nextSegment.GetComponent<SpringJoint2D>();

        nextSegmentJoint.connectedBody =
                this.GetComponent<Rigidbody2D>();

        // Usuwamy i niszczymy górny segment
        ropeSegments.RemoveAt(0);
        Destroy(topSegment);

    }

    // Metoda wykonywana podczas obsługi każdej ramki; 
    // wydłuża lub skraca linę w razie konieczności
    void Update()
    {

        // Pobieramy górny segment i jego złączenia
        GameObject topSegment = ropeSegments[0];
        SpringJoint2D topSegmentJoint =
                topSegment.GetComponent<SpringJoint2D>();

        if (isIncreasing)
        {

            // Wydłużamy linę; jeśli osiągnęła maksymalną długość,
            // dodajemy nowy segment; w przeciwnym razie wydłużamy
            // górny segment liny

            if (topSegmentJoint.distance >= maxRopeSegmentLength)
            {
                CreateRopeSegment();
            }
            else
            {
                topSegmentJoint.distance += ropeSpeed *
                            Time.deltaTime;
            }

        }

        if (isDecreasing)
        {

            // Skracamy linę. Jeśli długość jest bliska zeru, to usuwamy 
            // segment; w przeciwnym razie skracamy długość górnego 
            // segmentu liny

            if (topSegmentJoint.distance <= 0.005f)
            {
                RemoveRopeSegment();
            }
            else
            {
                topSegmentJoint.distance -= ropeSpeed *
                            Time.deltaTime;
            }

        }

        if (lineRenderer != null)
        {
            // Obiekt lineRenderer rysuje linię na podstawie 
            // kolekcji punktów. Te punkty muszą być zsynchroniozowane
            // z położeniem poszczególnych segmentów liny.

            // Liczba wierzchołków obiektu lineRenderer odpowiada
            // liczbie segmentów liny, a dodatkowo zawiera punkt u góry 
            // liny gdize znajduje się jej punkt zaczepienia oraz punkt
            // u dołu liny, w którym jest do niej przywiązany krasnal.
            lineRenderer.positionCount
                = ropeSegments.Count + 2;

            // Górny wierzchołek zawsze znajduje się w miescu gdzie zaczyna się  lina
            lineRenderer.SetPosition(0,
            this.transform.position);

            // Dla każdego segmentu liny rozmieszczamy na jego podstawie odpowiednie
            // segmenty wierzchołki używane przez lineRenderer
            for (int i = 0; i < ropeSegments.Count; i++)
            {
                lineRenderer.SetPosition(i + 1,
                    ropeSegments[i].transform.position);
            }

            // Ostatnim punkt jest połączony z punktem zaczepienia  obiektu
            SpringJoint2D connectedObjectJoint =
                connectedObject.GetComponent<SpringJoint2D>();
            lineRenderer.SetPosition(
                ropeSegments.Count + 1,
                connectedObject.transform.TransformPoint(
                        connectedObjectJoint.anchor)
            );
        }
    }
}
