using System.Collections;
using System.Collections.Generic;
using System.Net.Http.Headers;
using TMPro;
using UnityEngine;

public class CrossManager : BackButton
{
    [SerializeField] GraphManager graphManager;
    [SerializeField] GameObject graphs;
    [SerializeField] TextMeshPro[] crossTexts;
    [SerializeField] GameObject crossOptionMenu;
    [SerializeField] GameObject nonCrossOptionMenu;

    private GraphData[] graphDatas;
    //private Material[] materials;  マテリアルの保存...未実装
    private GraphData[] crossGraphDatas = new GraphData[3];


    public void updateGraphDatas(GraphData[] graphDatas)
    {
        this.graphDatas = graphDatas;
    }

    //呼ばれたら全て新しくする
    public void crossChecker(GraphData[] graphDatas)
    {
        foreach (TextMeshPro tm in crossTexts)
        {
            tm.text = "";
        }

        foreach (GraphData cross in crossGraphDatas)
        {
            if (cross != null)
            {
                if (cross.getgraphObject() != null)
                {
                    Destroy(cross.getgraphObject());
                }
            }
        }

        crossGraphDatas = new GraphData[3];
        updateGraphDatas(graphDatas);
        

        int numberOfGraphObjcet = 0;
        foreach (GraphData data in graphDatas)
        {
            if(data != null)
            {
                if (data.isDataListInputted())
                {
                    numberOfGraphObjcet++;
                }
            }
        }

        

        switch (numberOfGraphObjcet)
        {
            case 0:　//データ数0こ
            case 1:  //データ数1こ
                break;
            case 2: //データ数2こ,index = 0,1
                crossGraphDatas[0] = crossCalculate(graphDatas[0], graphDatas[1]);
                setCrossText(crossGraphDatas[0],0);
                break;
            case 3: //データ数3こ, index = 0,1,2
                crossGraphDatas[0] = crossCalculate(graphDatas[0], graphDatas[1]);
                crossGraphDatas[1] = crossCalculate(graphDatas[0], graphDatas[2]);
                crossGraphDatas[2] = crossCalculate(graphDatas[1], graphDatas[2]);
                
                for(int i = 0;i < 3; i++)
                {
                    setCrossText(crossGraphDatas[i],i);
                }
                break;
        }


    }

    private void setCrossText(GraphData data,int index)
    {
        crossTexts[index].fontSize = 0.06f;
        switch (data.getGraphNum())
        {
            case 0: //直線
                switch (data.getCrossMessage() % 10)
                {
                    case 0: //交線あり
                        crossTexts[index].text = "点(" + data.getLinePassingPoint().x + " ," + data.getLinePassingPoint().y + " ," + data.getLinePassingPoint().z + ")を通る" +
                        "\nベクトル(" + data.getLineDirection().x + " ," + data.getLineDirection().y + " ," + data.getLineDirection().z + ")に平行な直線";
                        crossTexts[index].fontSize = 0.06f;
                        break;
                    case 1: //平行
                        crossTexts[index].text = "平行";
                        break;
                    case 2: //全く同じ平面
                        crossTexts[index].text = "同一の平面";
                        break;
                }
                break;
            case 3: //点
                switch (data.getCrossMessage() % 10)
                {
                    case 0: //交点あり
                        crossTexts[index].text = "点("+data.getCrossPoint().x +", "+data.getCrossPoint().y+", "+data.getCrossPoint().z+")";
                        break;
                    case 1:
                        crossTexts[index].text = "平行";
                        break;
                    case 2:
                        crossTexts[index].text = "同一の直線";
                        break;
                    case 4:
                        if(data.getCrossMessage()/10 == 1)
                        {
                            crossTexts[index].text = "ねじれの位置";
                        }
                        else
                        {
                            crossTexts[index].text = "平面上の直線";
                        }
                        break;
                }
                break;
        }
        
    }


    private GraphData crossCalculate(GraphData data1,GraphData data2)
    {
        GraphData newerData = new GraphData();

        switch (data1.getGraphNum() + data2.getGraphNum()) {
            case 0: //直線と直線 ->　点
                newerData = crossLineLineCalculate(data1, data2);
                break;
            case 1: //直線と平面　-> 点
                switch (data1.getGraphNum())
                {
                    case 0: //data1が直線
                        newerData = crossLinePlaneCalculate(data1, data2);
                        break;
                    case 1: //data2が平面
                        newerData = crossLinePlaneCalculate(data2, data1);
                        break;
                }
                break;
            case 2: //平面と平面　-> 直線
                newerData = crossPlanePlaneCalculate(data1, data2);
                break;
        }

        return newerData;
    }

    private GraphData crossLineLineCalculate(GraphData l1, GraphData l2)
    {
        GraphData point = new GraphData();
        Vector3 outer = Vector3.Cross(l1.getLineDirection(), l2.getLineDirection());
        Vector3 l1p = l1.getLinePassingPoint();
        Vector3 l1d = l1.getLineDirection();
        Vector3 l2p = l2.getLinePassingPoint();
        Vector3 l2d = l2.getLineDirection(); ;

        //媒介変数
        float s = 0;
        float t = 0;

        point.setGraphNum(3);

        if (outer.z == 0) //x,y成分が平行
        {
            if(outer.x == 0)//y,z成分が平行
            {
                /*この時点でx,y,z成分が平行
                 * ->同じ直線の可能性。もしくは
                 * 平行で交点なし(ねじれの位置ではない)             
                 */

                //同じ直線かどうか -> l2の通過点をl1が通るかで判断
                if (l1d.x != 0) //x方向が0でない
                {
                    t =  (l2p.x - l1p.x)/l1d.x;
                    if ((l1p.y + t * l1d.y) == l2p.y)
                    {
                        if ((l1p.z + t * l1d.z) == l2p.z)
                        {
                            //通過点をもう片方が通る
                            point.setCrossMessage(12);
                            return point;
                        }
                    }
                    //平行でした
                    point.setCrossMessage(11);
                    return point;

                }
                else if (l1d.y != 0) //x方向が0で、y方向が0でない
                {
                    //xの通過点を比較
                    if (l1p.x != l2p.x)
                    {
                        point.setCrossMessage(11);
                        return point;
                    }

                    t = (l2p.y - l1p.y) / l1d.y;

                    if ((l1p.x + t * l1d.x) == l2p.x)
                    {
                        if ((l1p.z + t * l1d.z) == l2p.z)
                        {
                            //通過点をもう片方が通る
                            point.setCrossMessage(12);
                            return point;
                        }
                    }
                    //平行でした
                    point.setCrossMessage(11);
                    return point;
                }
                else //z方向が0でない
                {
                    //xの通過点を比較
                    if (l1p.x != l2p.x)
                    {
                        point.setCrossMessage(11);
                        return point;
                    }
                    //yの通過点を比較
                    if (l1p.y != l2p.y)
                    {
                        point.setCrossMessage(11);
                        return point;
                    }

                    t = (l2p.z - l1p.z) / l1d.z;

                    if ((l1p.y + t * l1d.y) == l2p.y)
                    {
                        if ((l1p.x + t * l1d.x) == l2p.x)
                        {
                            //通過点をもう片方が通る
                            point.setCrossMessage(12);
                            return point;
                        }
                    }
                    //平行でした
                    point.setCrossMessage(11);
                }

                //3成分が平行の際の結果
                return point;
            }

            /* x,y平面上でピッタリ重なっていなければ交わることはない
             */

            //ピッタリ重なるのか
            if (isLine1OverlapLine2(new Vector2(l1p.x,l1p.y), new Vector2(l1d.x, l1d.y), new Vector2(l2p.x, l2p.y), new Vector2(l2d.x, l2d.y)))
            {
                //必ず交点をもつ(平行処理は既実施)
                t = (l1d.z * (l1p.y - l2p.y) - l1d.y * (l1p.z - l2p.z))/(l1d.z*l2d.y-l1d.y*l2d.z);
                point.setCrossPoint(l2p + t*l2d);
                point.setCrossMessage(10);
            }
            else
            {
                point.setCrossMessage(14);
                return point;
            }
        }
        else if(outer.x == 0) //x,y成分は平行ではないが、y,z成分は平行
        {
            /*y,z平面上で重なっていなければ交わることはない
             */

            //ピッタリ重なるのか
            if (isLine1OverlapLine2(new Vector2(l1p.y, l1p.z), new Vector2(l1d.y, l1d.z), new Vector2(l2p.y, l2p.z), new Vector2(l2d.y, l2d.z)))
            {
                //必ず交点をもつ
                t = (l1d.x * (l1p.y - l2p.y) - l1d.y * (l1p.x - l2p.x)) / (l1d.x * l2d.y - l1d.y * l2d.x);
                point.setCrossPoint(l2p + t * l2d);
                point.setCrossMessage(10);
            }
            else
            {
                point.setCrossMessage(14);
                return point;
            }

        }
        else //全て平行ではない -> 媒介変数ごり押し計算
        {
            //x,y成分にて
            s = (l2d.x * (l1p.y - l2p.y) - l2d.y * (l1p.x - l2p.x)) / (l1d.x * l2d.y - l1d.y * l2d.x);
            t = (l1d.x * (l1p.y - l2p.y) - l1d.y * (l1p.x - l2p.x)) / (l1d.x * l2d.y - l1d.y * l2d.x);

            //z成分の左辺、右辺で判断
            if ((l1p.z + s*l1d.z)==(l2p.z + t*l2d.z))
            {
                //媒介変数を入れた点が左右で一致
                point.setCrossPoint(l1p + s*l1d);
                point.setCrossMessage(10);
            }
            else
            {
                point.setCrossMessage(14);
                return point;
            }
        }


        
        point.setDataFormulaInputted(true);
        GameObject g = GameObject.CreatePrimitive(PrimitiveType.Sphere);
        g.transform.parent = graphs.transform;
        g.transform.localPosition = point.getCrossPoint();
        g.transform.localScale = new Vector3(0.03f, 0.03f, 0.03f);
        Material m = g.GetComponent<MeshRenderer>().material;
        m.color =  new Color(1, 0, 0, 1);
        point.setGraphObject(g);

        return point;
    }

    //平行な2直線がピッタリ重なるのか
    private bool isLine1OverlapLine2(Vector2 l1p,Vector2 l1d, Vector2 l2p, Vector2 l2d)
    {
        bool b = false;

        if (l1d.x == 0)
        {
            if(l1p.x == l2p.x)
            {
                b = true;
            }
        }
        else
        {
            float yIntercept1 = l1p.y - (l1p.y * l1d.y) / l1d.x;
            float yIntercept2 = l2p.y - (l2p.y * l2d.y) / l2d.x;
            if(yIntercept1 == yIntercept2)
            {
                b = true;
            }
        }

        return b;
    }

    private GraphData crossLinePlaneCalculate(GraphData l,GraphData p)
    {
        GraphData point = new GraphData();
        Vector3 p3v = new Vector3(p.getPlaneCoefficient().x, p.getPlaneCoefficient().y, p.getPlaneCoefficient().z);
        point.setGraphNum(3);

        //平行か->方向ベクトル、法線ベクトルの内積が0
        if (Vector3.Dot(l.getLineDirection(),p3v) == 0)
        {
            point.setCrossMessage(21);

            float d = l.getLinePassingPoint().x * p3v.x
                + l.getLinePassingPoint().y * p3v.y
                + l.getLinePassingPoint().z * p3v.z;

            //同一平面上か
            if(d == -p.getPlaneCoefficient().w)
            {
                point.setCrossMessage(23);
            }

            return point;
        }

        Vector3 lp = l.getLinePassingPoint();
        Vector3 ld = l.getLineDirection();

        float uUpper = p3v.x * lp.x + p3v.y * lp.y + p3v.z * lp.z + p.getPlaneCoefficient().w;
        float uLower = p3v.x * ld.x + p3v.y * ld.y + p3v.z * ld.z;
        float u = 1;
        if (uLower != 0) {
            u = -uUpper / uLower;
        }
        else
        {
            Debug.Log("内積が0処理ミスエラー");
            return null;
        }

        point.setCrossPoint(u * ld + lp);
        point.setGraphNum(3);
        point.setDataFormulaInputted(true);
        point.setCrossMessage(20);
        GameObject s = GameObject.CreatePrimitive(PrimitiveType.Sphere);
        s.transform.parent = graphs.transform;
        s.transform.localPosition = point.getCrossPoint();
        s.transform.localScale = new Vector3(0.03f,0.03f,0.03f);
        Material m = s.GetComponent<MeshRenderer>().material;
        m.color = new Color(1, 0, 0, 1);
        point.setGraphObject(s);

        return point;
    }
    private GraphData crossPlanePlaneCalculate(GraphData plane1, GraphData plane2)
    {
        GraphData line = new GraphData();
        Vector4 p1 = plane1.getPlaneCoefficient();
        Vector4 p2 = plane2.getPlaneCoefficient();

        Vector3 p1v3 = new Vector3(p1.x, p1.y,p1.z);
        Vector3 p2v3 = new Vector3(p2.x, p2.y, p2.z);
        line.setGraphNum(0);

        //位置関係判断
        if (Vector3.Cross(p1v3,p2v3) == Vector3.zero)
        {
            if (p1.w == p2.w)
            {
                line.setCrossMessage(32);
            }
            else
            {
                line.setCrossMessage(31);
            }

            return line;
        }
        
        float ab = p1.x * p2.y - p1.y * p2.x;
        float bc = p1.y * p2.z - p1.z * p2.y;
        float ca = p1.z * p2.x - p1.x * p2.z;
        float ad = p1.x * p2.w - p1.w * p2.x;
        float bd = p1.y * p2.w - p1.w * p2.y;
        float cd = p1.z * p2.w - p1.w * p2.z;
        
        if(ca != 0)
        {
            line.setLinePassingPoint(new Vector3(-cd / ca, 0, ad / ca));
            line.setCrossMessage(30);
        }
        else if (ab != 0)
        {
            line.setLinePassingPoint(new Vector3(bd / ab, -ad / ab, 0));
            line.setCrossMessage(30);
        }
        
        line.setLineDirection(Vector3.Cross(p1v3, p2v3));
        line.setGraphNum(0);
        line.setDataFormulaInputted(true);

        Vector3 inLinePoint = new Vector3();
        GameObject ln = GameObject.CreatePrimitive(PrimitiveType.Cylinder);
        ln.transform.parent = graphs.transform;

        if (line.getLineDirection().y != 0)
        {
            float uY0 = -1 * line.getLinePassingPoint().y / line.getLineDirection().y;

            inLinePoint = new Vector3(
                line.getLinePassingPoint().x + uY0 * line.getLineDirection().x,
                0,
                line.getLinePassingPoint().z + uY0 * line.getLineDirection().z);

        }
        else if (line.getLineDirection().x != 0)
        {
            float uX0 = -1 * line.getLinePassingPoint().x / line.getLineDirection().x;

            inLinePoint = new Vector3(
                0,
                line.getLinePassingPoint().y + uX0 * line.getLineDirection().y,
                line.getLinePassingPoint().z + uX0 * line.getLineDirection().z);

        }
        else if (line.getLineDirection().z != 0)
        {
            float uZ0 = -1 * line.getLinePassingPoint().z / line.getLineDirection().z;

            inLinePoint = new Vector3(
                line.getLinePassingPoint().x + uZ0 * line.getLineDirection().x,
                line.getLinePassingPoint().y + uZ0 * line.getLineDirection().y,
                0);

        }
        else
        {
            //全て0の場合
            //たぶんない。処理済みなはず
            Debug.Log("係数が無効です");
            return null;
        }

        ln.transform.localRotation = Quaternion.FromToRotation(Vector3.up, line.getLineDirection());
        ln.transform.localPosition = inLinePoint;
        ln.transform.localScale = new Vector3(0.01f, 20, 0.01f);
        Material m = ln.GetComponent<MeshRenderer>().material;
        m.color = new Color(1, 0, 0, 1);
        line.setGraphObject(ln);

        return line;
    }

    public void pressedButton(int i)
    {
        if (crossGraphDatas[i] != null)
        {

            if (crossGraphDatas[i].isDataFormulaInputted())
            {
                switch (i)
                {
                    case 0:
                        crossOptionMenu.SetActive(true);
                        crossOptionMenu.GetComponent<CrossOptionMenu>().Initialize(crossGraphDatas[i], graphDatas[0], graphDatas[1]);
                        base.anotherButtonAction();
                        break;
                    case 1:
                        crossOptionMenu.SetActive(true);
                        crossOptionMenu.GetComponent<CrossOptionMenu>().Initialize(crossGraphDatas[i], graphDatas[0], graphDatas[2]);
                        base.anotherButtonAction();
                        break;
                    case 2:
                        crossOptionMenu.SetActive(true);
                        crossOptionMenu.GetComponent<CrossOptionMenu>().Initialize(crossGraphDatas[i], graphDatas[1], graphDatas[2]);
                        base.anotherButtonAction();
                        break;
                }
            }
            else
            {
                switch (i)
                {
                    case 0:
                        nonCrossOptionMenu.SetActive(true);
                        nonCrossOptionMenu.GetComponent<NonCrossOptionMenu>().Initialize(crossGraphDatas[i], graphDatas[0], graphDatas[1]);
                        base.anotherButtonAction();
                        break;
                    case 1:
                        nonCrossOptionMenu.SetActive(true);
                        nonCrossOptionMenu.GetComponent<NonCrossOptionMenu>().Initialize(crossGraphDatas[i], graphDatas[0], graphDatas[2]);
                        base.anotherButtonAction();
                        break;
                    case 2:
                        nonCrossOptionMenu.SetActive(true);
                        nonCrossOptionMenu.GetComponent<NonCrossOptionMenu>().Initialize(crossGraphDatas[i], graphDatas[1], graphDatas[2]);
                        base.anotherButtonAction();
                        break;
                }
                
            }
        }
    }
    
}
