package jp.sourceforge.acerola3d.a3editor;

import java.util.*;
import javax.vecmath.*;

import jp.sourceforge.acerola3d.a3.*;
import jp.sourceforge.acerola3d.a3.bvh.BVH;
import jp.sourceforge.acerola3d.a3.catalog.*;

class A3eActionEditor {
    A3Editor editor;
    A3eFileManager fileManager;
    Catalog catalog;
    A3eActionEditorGUI gui;
    A currentAction;

    A3eActionEditor(A3Editor e) {
        editor = e;
        fileManager = e.fileManager;
        catalog = e.catalog;
        setCurrent(catalog.a3.getA().get(0));
        gui = new A3eActionEditorGUI(this,editor);
    }
    String getBVHPath() {
        return currentAction.getBvh();
    }
    String getBVHURL() {
        return fileManager.path2UrlString(currentAction.getBvh());
    }
    void setCurrent(A a) {
        currentAction = a;
    }
    void changeCurrentAction(String an) {
        A ca = null;
        for (A a : catalog.a3.getA()) {
            if (a.getAn().equals(an)) {
                ca = a;
                break;
            }
        }
        if (ca!=null) {
            setCurrent(ca);
        }
    }
    A3eActionEditorGUI getGUI() {
        return gui;
    }
    String[] getActionNames() {
        ArrayList<String> al = new ArrayList<String>();
        for (A a : catalog.a3.getA()) {
            al.add(a.getAn());
        }
        return al.toArray(new String[0]);
    }
    void deleteAction(String an) {
        List<A> actions = catalog.a3.getA();
        if (actions.size()<=1)
            return;
        A a = null;
        for (A aa : actions) {
            if (aa.getAn().equals(an)) {
                a = aa;
                break;
            }
        }
        if (a!=null)
            actions.remove(a);
        setCurrent(actions.get(0));
    }
    void createNewAction(String an) {
        A a = new A();
        a.setAn(an);
        catalog.a3.getA().add(a);
        setCurrent(a);
    }
    boolean checkCompatibility(String an,A3eFile bvh) {
        if (an.equals("[none]"))
            return true;
        try {
            A baseAction=null;
            for (A a:catalog.a3.getA()) {
                if (a.getAn().equals(an)) {
                    baseAction = a;
                    break;
                }
            }
            if (baseAction==null)
                return false;
            A3eFile f = editor.fileManager.getA3eFileFromPath(baseAction.getBvh());
            BVH baseBvh = new BVH(f.toFile().toURI().toURL().toString());
            BVH targetBVH = new BVH(bvh.toFile().toURI().toURL().toString());
            for (String b:baseBvh.getAllBones()) {
                boolean check = false;
                for (String bb:targetBVH.getAllBones()) {
                    if (b.equals(bb)) {
                        check=true;
                        break;
                    }
                }
                if (check==false)
                    return false;
            }
            return true;
        } catch (Exception e) {
            return false;
        }
    }
    void createNewAction2(String newActionName,String baseActionName,A3eFile bvh) {
        A baseAction=null;
        if (!baseActionName.equals("[none]")) {
            for (A a:catalog.a3.getA()) {
                if (a.getAn().equals(baseActionName)) {
                    baseAction = a;
                    break;
                }
            }
        }
        if (baseAction==null) {
            A a = new A();
            a.setAn(newActionName);
            if (bvh!=A3eFile.noneFile)
                a.setBvh(bvh.getPath());
            catalog.a3.getA().add(a);
            setCurrent(a);
        } else {
            A a = new A();
            a.setAn(newActionName);
            if (bvh!=A3eFile.noneFile)
                a.setBvh(bvh.getPath());
            //-----
            for (P p:baseAction.getP()) {
                P pp = new P();
                pp.setName(p.getName());
                pp.setWrl(p.getWrl());
                pp.setScale(p.getScale());
                pp.setOffset(p.getOffset());
                pp.setRot(p.getRot());
                a.getP().add(pp);
            }
            if (baseAction.getS()!=null) {
                S s = new S();
                s.setFile(baseAction.getS().getFile());
                s.setType(baseAction.getS().getType());
                s.setLoop(baseAction.getS().isLoop());
                s.setGain(baseAction.getS().getGain());
                s.setOffset(baseAction.getS().getOffset());
                s.setDirection(baseAction.getS().getDirection());
                s.setContinue(baseAction.getS().isContinue());
                a.setS(s);
            }
            a.setLoop(baseAction.isLoop());
            a.setScale(baseAction.getScale());
            a.setOffset(baseAction.getOffset());
            a.setRot(baseAction.getRot());
            a.setRightBalloonOffset(baseAction.getRightBalloonOffset());
            a.setLeftBalloonOffset(baseAction.getLeftBalloonOffset());
            a.setTopBalloonOffset(baseAction.getTopBalloonOffset());
            a.setBottomBalloonOffset(baseAction.getBottomBalloonOffset());
            a.setLabelOffset(baseAction.getLabelOffset());
            a.setSegno(baseAction.getSegno());
            a.setDalsegno(baseAction.getDalsegno());
            //-----
            catalog.a3.getA().add(a);
            setCurrent(a);
        }
    }
    void setActionName(String an) {
        currentAction.setAn(an);
    }
    String getCurrentActionName() {
        return currentAction.getAn();
    }
    boolean checkActionNameDuplication(String an) {
        for (A a : catalog.a3.getA()) {
            if (a.getAn().equals(an))
                return true;
        }
        return false;
    }
    //以下のメソッドは全てcurrentActionに対する情報のgetter,setter
    String getBVH() {
        String bvhFilePath = currentAction.getBvh();
        String fileName = fileManager.path2FileName(bvhFilePath);
        return fileName;
    }
    A3eFile getBVHFile() {
        String bvhFilePath = currentAction.getBvh();
        if (bvhFilePath!=null)
            return fileManager.getA3eFileFromPath(bvhFilePath);
        return A3eFile.noneFile;
    }
    void setBVH(A3eFile file) {
        String an = currentAction.getAn();
        int i = catalog.a3.getA().indexOf(currentAction);
        catalog.a3.getA().remove(currentAction);
        A a = new A();
        a.setAn(an);
        catalog.a3.getA().add(i,a);
        if (file==A3eFile.noneFile)
            a.setBvh("[none]");
        else
            a.setBvh(file.getPath());
        setCurrent(a);
    }
    boolean getLoop() {
        return currentAction.isLoop();
    }
    void setLoop(boolean b) {
        currentAction.setLoop(b);
    }
    double getSegno() {
        return currentAction.getSegno();
    }
    void setSegno(double d) {
        currentAction.setSegno(d);
    }
    double getDalsegno() {
        return currentAction.getDalsegno();
    }
    void setDalsegno(double d) {
        currentAction.setDalsegno(d);
    }
    double[] getRightBalloonOffset() {//右の吹き出し
        return string2vector2d(currentAction.getRightBalloonOffset());
    }
    void setRightBalloonOffset(double[] v) {//右の吹き出し
        currentAction.setRightBalloonOffset(vector2d2string(v));
    }
    double[] getLeftBalloonOffset() {//左の吹き出し
        return string2vector2d(currentAction.getLeftBalloonOffset());
    }
    void setLeftBalloonOffset(double[] v) {//左の吹き出し
        currentAction.setLeftBalloonOffset(vector2d2string(v));
    }
    double[] getTopBalloonOffset() {//上の吹き出し
        return string2vector2d(currentAction.getTopBalloonOffset());
    }
    void setTopBalloonOffset(double v[]) {//上の吹き出し
        currentAction.setTopBalloonOffset(vector2d2string(v));
    }
    double[] getBottomBalloonOffset() {//下の吹き出し
        return string2vector2d(currentAction.getBottomBalloonOffset());
    }
    void setBottomBalloonOffset(double v[]) {//上の吹き出し
        currentAction.setBottomBalloonOffset(vector2d2string(v));
    }
    double[] getLabelOffset() {//ラベル
        return string2vector2d(currentAction.getLabelOffset());
    }
    void setLabelOffset(double v[]) {//ラベル
        currentAction.setLabelOffset(vector2d2string(v));
    }
    String[] getUsedBones() {
        ArrayList<String> al = new ArrayList<String>();
        for (P p : currentAction.getP()) {
            al.add(p.getName());
        }
        return al.toArray(new String[0]);
    }
    A3eFile getVRMLFile(String bn) {
        String wrl = null;
        for (P p : currentAction.getP()) {
            if (p.getName().equals(bn)) {
                wrl = p.getWrl();
            }
        }
        if (wrl!=null)
            return fileManager.getA3eFileFromPath(wrl);
        return A3eFile.noneFile;
    }
    void setVRML(String bn,A3eFile file) {
        P p = null;
        for (P pp : currentAction.getP()) {
            if (pp.getName().equals(bn)) {
                p = pp;
                break;
            }
        }
        if (p!=null) {
            if (file==A3eFile.noneFile) {
                currentAction.getP().remove(p);
            } else {
                p.setWrl(file.getPath());
            }
        } else {
            if (file!=A3eFile.noneFile) {
                p = new P();
                p.setName(bn);
                p.setWrl(file.getPath());
                currentAction.getP().add(p);
            }
        }
    }
    void removeSoundDate() {
        currentAction.setS(null);
    }
    A3eFile getSoundFile() {
        S s = currentAction.getS();
        if (s==null)
            return A3eFile.noneFile;
        String sf = currentAction.getS().getFile();
        A3eFile f = fileManager.getA3eFileFromPath(sf);
        if (f!=null)
            return f;
        return A3eFile.noneFile;
    }
    void setSoundFile(A3eFile file) {
        S s = currentAction.getS();
        if (s==null) {
            s = new S();
            s.setFile(file.getPath());
            s.setType(SoundType.POINT_SOUND);
            currentAction.setS(s);
        }
        s.setFile(file.getPath());
    }
    String getSoundType() {
        SoundType st = currentAction.getS().getType();
        if (st == SoundType.POINT_SOUND){
            return "PointSound";
        } else if (st == SoundType.BACKGROUND_SOUND) {
            return "Background";
        } else if (st == SoundType.CONE_SOUND) {
            return "ConeSound";
        } else {
            return null;
        }
    }
    void setSoundType(String st) {
        if (st.equals("PointSound")) {
            currentAction.getS().setType(SoundType.POINT_SOUND);
        } else if (st.equals("Background")) {
            currentAction.getS().setType(SoundType.POINT_SOUND);
        } else if (st.equals("ConeSound")) {
            currentAction.getS().setType(SoundType.CONE_SOUND);
        }
    }
    boolean getSoundLoop() {
        return currentAction.getS().isLoop();
    }
    void setSoundLoop(boolean b) {
        currentAction.getS().setLoop(b);
    }
    double getSoundGain() {
        return currentAction.getS().getGain();
    }
    void setSoundGain(double d) {
        currentAction.getS().setGain(d);
    }
    boolean getSoundContinue() {
        return currentAction.getS().isContinue();
    }
    void setSoundContinue(boolean b) {
        currentAction.getS().setContinue(b);
    }
    Vector3d getSoundOffset() {
        return string2vector3d(currentAction.getS().getOffset());
    }
    void setSoundOffset(double x,double y,double z) {
        S s = currentAction.getS();
        if (s==null)
            return;
        String so = ""+x+" "+y+" "+z;
        s.setOffset(so);
    }
    Vector3d getSoundDirection() {
        return string2vector3d(currentAction.getS().getDirection());
    }
    void setSoundDirection(double x,double y,double z) {
        S s = currentAction.getS();
        if (s==null)
            return;
        String sd = ""+x+" "+y+" "+z;
        s.setDirection(sd);
    }
    Vector3d getOffset() {
        return string2vector3d(currentAction.getOffset());
    }
    void setOffset(Vector3d v) {
        currentAction.setOffset(vector3d2string(v));
    }
    //返ってくるのはデグリー
    Vector3d getRot() {
        return string2vector3d(currentAction.getRot());
    }
    //引数はデグリーで与えること
    void setRot(Vector3d v) {
        currentAction.setRot(vector3d2string(v));
    }
    Quat4d getQuat() {
        Vector3d v = string2vector3d(currentAction.getRot());
        v.scale(Math.PI/180.0);
        return Util.euler2quat(v);
    }
    void setQuat(Quat4d quat) {
        Vector3d v = Util.quat2euler(quat);
        v.scale(180.0/Math.PI);
        currentAction.setRot(vector3d2string(v));
    }
    double getScale() {
        return currentAction.getScale();
    }
    void setScale(double s) {
        currentAction.setScale(s);
    }


    //返り値はCATALOGの生データ(rot(度)はquatに変換)
    T getPartData(String bn) {
        P part = null;
        for (P p : currentAction.getP()) {
            if (p.getName().equals(bn)) {
                part = p;
                break;
            }
        }
        if (part==null)
            return null;
        T t = new T();
        t.offset = string2vector3d(part.getOffset());
        Vector3d v = string2vector3d(part.getRot());
        v.scale(Math.PI/180.0);
        t.quat = Util.euler2quat(v);
        t.scale = part.getScale();
        return t;
    }
    //引数はCATALOG.XMLにそのまま書き込まれる(quatはrot(度)に変換)
    void setPartData(String bn,T t) {
        P part = null;
        for (P p : currentAction.getP()) {
            if (p.getName().equals(bn)) {
                part = p;
                break;
            }
        }
        if (part==null)
            return;
        part.setOffset(vector3d2string(t.offset));
        Vector3d v = Util.quat2euler(t.quat);
        v.scale(180.0/Math.PI);
        part.setRot(vector3d2string(v));
        part.setScale(t.scale);
    }
    /*
    void updateActionOffsetRotScale() {
        if (currentSkeleton==null)
            return;
        setOffset(currentSkeleton.getLoc());
        setRot(currentSkeleton.getRot());
        setScale(currentSkeleton.getScale());
    }
    void updatePartsOffsetRotScale() {
        Set<String> keys = vrmls.keySet();
        for (String k: keys) {
            updatePartOffsetRotScale(k);
        }
    }
    void updatePartOffsetRotScale(String bn) {
        Vector3d actionLoc;
        Quat4d actionQuatRev;
        double actionScale;
        Vector3d bvhOffset;
        if (currentSkeleton!=null) {
            actionLoc = currentSkeleton.getLoc();
            actionQuatRev = currentSkeleton.getQuat();
            actionQuatRev.conjugate();
            actionScale = currentSkeleton.getScale();
            bvhOffset = currentSkeleton.getOffset(bn);
            if (bvhOffset==null) // bnが[none]の場合(だけだと思う)
                bvhOffset = new Vector3d();
        } else {
            actionLoc = new Vector3d();
            actionQuatRev = new Quat4d(0.0,0.0,0.0,1.0);
            actionScale = 1.0;
            bvhOffset = new Vector3d();
        }
        for (P p:currentAction.getP()) {
            if (p.getName().equals(bn)) {
                CenteredVRML vrml = vrmls.get(bn);
                Vector3d v = vrml.getLoc2();
                v.sub(actionLoc);
                v.scale(1.0/actionScale);
                v = Util.trans(actionQuatRev,v);
                v.sub(bvhOffset);
                p.setOffset(vector3d2string(v));

                Quat4d boneQuat = vrml.getQuat();
                boneQuat.mul(actionQuatRev,boneQuat);
                v = Util.quat2euler(boneQuat);
                v.scale(180.0/Math.PI);
                p.setRot(vector3d2string(v));
                p.setScale(vrml.getScale()/actionScale);
            }
        }
    }
    */
    void saveCatalog() {
        try {
            editor.catalog.save();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    Vector3d string2vector3d(String s) {
        String ss[] = s.split("\\s+");
        double x = Double.parseDouble(ss[0]);
        double y = Double.parseDouble(ss[1]);
        double z = Double.parseDouble(ss[2]);
        return new Vector3d(x,y,z);
    }
    String vector3d2string(Vector3d v) {
        return ""+v.x+" "+v.y+" "+v.z;
    }
    double[] string2vector2d(String s) {
        String ss[] = s.split("\\s+");
        double ret[] = new double[2];
        ret[0] = Double.parseDouble(ss[0]);
        ret[1] = Double.parseDouble(ss[1]);
        return ret;
    }
    String vector2d2string(double v[]) {
        return ""+v[0]+" "+v[1];
    }
}
//Offset,Quat,Scaleをひとまとめにしたクラス
class T {
    Vector3d offset; Quat4d quat; double scale;
}
