/*
 * Copyright (c) 2009-2012 jMonkeyEngine
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 * * Redistributions of source code must retain the above copyright
 *   notice, this list of conditions and the following disclaimer.
 *
 * * Redistributions in binary form must reproduce the above copyright
 *   notice, this list of conditions and the following disclaimer in the
 *   documentation and/or other materials provided with the distribution.
 *
 * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
 *   may be used to endorse or promote products derived from this software
 *   without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package jme3tools.converters.model.strip;

/**
 * 
 */
class StripInfo {

    StripStartInfo m_startInfo;
    FaceInfoVec    m_faces = new FaceInfoVec();
    int              m_stripId;
    int              m_experimentId;
    
    boolean visited;

    int m_numDegenerates;
    

    public StripInfo(StripStartInfo startInfo,int stripId, int experimentId) {

        m_startInfo = startInfo;
        m_stripId      = stripId;
        m_experimentId = experimentId;
        visited = false;
        m_numDegenerates = 0;
    }

    boolean isExperiment() {
        return m_experimentId >= 0;
    }
    
    boolean isInStrip(FaceInfo faceInfo) {
        if(faceInfo == null)
            return false;
        
        return (m_experimentId >= 0 ? faceInfo.m_testStripId == m_stripId : faceInfo.m_stripId == m_stripId);
    }
    

///////////////////////////////////////////////////////////////////////////////////////////
// IsMarked()
//
// If either the faceInfo has a real strip index because it is
// already assign to a committed strip OR it is assigned in an
// experiment and the experiment index is the one we are building
// for, then it is marked and unavailable
    boolean isMarked(FaceInfo faceInfo){
        return (faceInfo.m_stripId >= 0) || (isExperiment() && faceInfo.m_experimentId == m_experimentId);
    }


///////////////////////////////////////////////////////////////////////////////////////////
// MarkTriangle()
//
// Marks the face with the current strip ID
//
    void markTriangle(FaceInfo faceInfo){
        if (isExperiment()){
            faceInfo.m_experimentId = m_experimentId;
            faceInfo.m_testStripId  = m_stripId;
        }
        else{
            faceInfo.m_experimentId = -1;
            faceInfo.m_stripId      = m_stripId;
        }
    }


    boolean unique(FaceInfoVec faceVec, FaceInfo face)
    {
        boolean bv0, bv1, bv2; //bools to indicate whether a vertex is in the faceVec or not
        bv0 = bv1 = bv2 = false;

        for(int i = 0; i < faceVec.size(); i++)
           {
            if(!bv0)
               {
                if( (faceVec.at(i).m_v0 == face.m_v0) || 
                        (faceVec.at(i).m_v1 == face.m_v0) ||
                        (faceVec.at(i).m_v2 == face.m_v0) )
                    bv0 = true;
            }

            if(!bv1)
               {
                if( (faceVec.at(i).m_v0 == face.m_v1) || 
                        (faceVec.at(i).m_v1 == face.m_v1) ||
                        (faceVec.at(i).m_v2 == face.m_v1) )
                    bv1 = true;
            }

            if(!bv2)
               {
                if( (faceVec.at(i).m_v0 == face.m_v2) || 
                        (faceVec.at(i).m_v1 == face.m_v2) ||
                        (faceVec.at(i).m_v2 == face.m_v2) )
                    bv2 = true;
            }

            //the face is not unique, all it's vertices exist in the face vector
            if(bv0 && bv1 && bv2)
                return false;
        }
        
        //if we get out here, it's unique
        return true;
    }


///////////////////////////////////////////////////////////////////////////////////////////
// Build()
//
// Builds a strip forward as far as we can go, then builds backwards, and joins the two lists
//
    void build(EdgeInfoVec edgeInfos, FaceInfoVec faceInfos)
    {
        // used in building the strips forward and backward
        IntVec scratchIndices = new IntVec();
        
        // build forward... start with the initial face
        FaceInfoVec forwardFaces = new FaceInfoVec();
        FaceInfoVec backwardFaces = new FaceInfoVec();
        forwardFaces.add(m_startInfo.m_startFace);

        markTriangle(m_startInfo.m_startFace);
        
        int v0 = (m_startInfo.m_toV1 ? m_startInfo.m_startEdge.m_v0 : m_startInfo.m_startEdge.m_v1);
        int v1 = (m_startInfo.m_toV1 ? m_startInfo.m_startEdge.m_v1 : m_startInfo.m_startEdge.m_v0);
        
        // easiest way to get v2 is to use this function which requires the
        // other indices to already be in the list.
        scratchIndices.add(v0);
        scratchIndices.add(v1);
        int v2 = Stripifier.getNextIndex(scratchIndices, m_startInfo.m_startFace);
        scratchIndices.add(v2);

        //
        // build the forward list
        //
        int nv0 = v1;
        int nv1 = v2;

        FaceInfo nextFace = Stripifier.findOtherFace(edgeInfos, nv0, nv1, m_startInfo.m_startFace);
        while (nextFace != null && !isMarked(nextFace))
           {
            //check to see if this next face is going to cause us to die soon
            int testnv0 = nv1;
            int testnv1 = Stripifier.getNextIndex(scratchIndices, nextFace);
            
            FaceInfo nextNextFace = Stripifier.findOtherFace(edgeInfos, testnv0, testnv1, nextFace);

            if( (nextNextFace == null) || (isMarked(nextNextFace)) )
               {
                //uh, oh, we're following a dead end, try swapping
                FaceInfo testNextFace = Stripifier.findOtherFace(edgeInfos, nv0, testnv1, nextFace);

                if( ((testNextFace != null) && !isMarked(testNextFace)) )
                   {
                    //we only swap if it buys us something
                    
                    //add a "fake" degenerate face
                    FaceInfo tempFace = new FaceInfo(nv0, nv1, nv0);
                    
                    forwardFaces.add(tempFace);
                    markTriangle(tempFace);

                    scratchIndices.add(nv0);
                    testnv0 = nv0;

                    ++m_numDegenerates;
                }

            }

            // add this to the strip
            forwardFaces.add(nextFace);

            markTriangle(nextFace);
            
            // add the index
            //nv0 = nv1;
            //nv1 = NvStripifier::GetNextIndex(scratchIndices, nextFace);
            scratchIndices.add(testnv1);
            
            // and get the next face
            nv0 = testnv0;
            nv1 = testnv1;

            nextFace = Stripifier.findOtherFace(edgeInfos, nv0, nv1, nextFace);
            
        }
        
        // tempAllFaces is going to be forwardFaces + backwardFaces
        // it's used for Unique()
        FaceInfoVec tempAllFaces = new FaceInfoVec();
        for(int i = 0; i < forwardFaces.size(); i++)
            tempAllFaces.add(forwardFaces.at(i));

        //
        // reset the indices for building the strip backwards and do so
        //
        scratchIndices.clear();
        scratchIndices.add(v2);
        scratchIndices.add(v1);
        scratchIndices.add(v0);
        nv0 = v1;
        nv1 = v0;
        nextFace = Stripifier.findOtherFace(edgeInfos, nv0, nv1, m_startInfo.m_startFace);
        while (nextFace != null && !isMarked(nextFace))
           {
            //this tests to see if a face is "unique", meaning that its vertices aren't already in the list
            // so, strips which "wrap-around" are not allowed
            if(!unique(tempAllFaces, nextFace))
                break;

            //check to see if this next face is going to cause us to die soon
            int testnv0 = nv1;
            int testnv1 = Stripifier.getNextIndex(scratchIndices, nextFace);
            
            FaceInfo nextNextFace = Stripifier.findOtherFace(edgeInfos, testnv0, testnv1, nextFace);

            if( (nextNextFace == null) || (isMarked(nextNextFace)) )
               {
                //uh, oh, we're following a dead end, try swapping
                FaceInfo testNextFace = Stripifier.findOtherFace(edgeInfos, nv0, testnv1, nextFace);
                if( ((testNextFace != null) && !isMarked(testNextFace)) )
                   {
                    //we only swap if it buys us something
                    
                    //add a "fake" degenerate face
                    FaceInfo tempFace = new FaceInfo(nv0, nv1, nv0);

                    backwardFaces.add(tempFace);
                    markTriangle(tempFace);
                    scratchIndices.add(nv0);
                    testnv0 = nv0;

                    ++m_numDegenerates;
                }
                
            }

            // add this to the strip
            backwardFaces.add(nextFace);
            
            //this is just so Unique() will work
            tempAllFaces.add(nextFace);

            markTriangle(nextFace);
            
            // add the index
            //nv0 = nv1;
            //nv1 = NvStripifier::GetNextIndex(scratchIndices, nextFace);
            scratchIndices.add(testnv1);
            
            // and get the next face
            nv0 = testnv0;
            nv1 = testnv1;
            nextFace = Stripifier.findOtherFace(edgeInfos, nv0, nv1, nextFace);
        }
        
        // Combine the forward and backwards stripification lists and put into our own face vector
        combine(forwardFaces, backwardFaces);
    }


///////////////////////////////////////////////////////////////////////////////////////////
// Combine()
//
// Combines the two input face vectors and puts the result into m_faces
//
    void combine(FaceInfoVec forward, FaceInfoVec backward){
        
        // add backward faces
        int numFaces = backward.size();
        for (int i = numFaces - 1; i >= 0; i--)
            m_faces.add(backward.at(i));
        
        // add forward faces
        numFaces = forward.size();
        for (int i = 0; i < numFaces; i++)
            m_faces.add(forward.at(i));
    }


///////////////////////////////////////////////////////////////////////////////////////////
// SharesEdge()
//
// Returns true if the input face and the current strip share an edge
//
    boolean sharesEdge(FaceInfo faceInfo, EdgeInfoVec edgeInfos)
    {
        //check v0.v1 edge
        EdgeInfo currEdge = Stripifier.findEdgeInfo(edgeInfos, faceInfo.m_v0, faceInfo.m_v1);
        
        if(isInStrip(currEdge.m_face0) || isInStrip(currEdge.m_face1))
            return true;
        
        //check v1.v2 edge
        currEdge = Stripifier.findEdgeInfo(edgeInfos, faceInfo.m_v1, faceInfo.m_v2);
        
        if(isInStrip(currEdge.m_face0) || isInStrip(currEdge.m_face1))
            return true;
        
        //check v2.v0 edge
        currEdge = Stripifier.findEdgeInfo(edgeInfos, faceInfo.m_v2, faceInfo.m_v0);
        
        if(isInStrip(currEdge.m_face0) || isInStrip(currEdge.m_face1))
            return true;
        
        return false;
        
    }

    
    
    
    

    
    
    
    
}
