//--------------------------------------------------------------
void ofxThreadedVideo::setVolume(int _volume){
    Poco::ScopedLock<ofMutex> lock();
    if(currentVideoID != VIDEO_NONE && loadVideoID == VIDEO_NONE){
        volume[currentVideoID] = _volume;
        videos[currentVideoID].setVolume(volume[currentVideoID]);
    }
    volume[getNextLoadID()] = _volume;
    videos[getNextLoadID()].setVolume(volume[getNextLoadID()]);
}
//--------------------------------------------------------------
void ofxThreadedVideo::setPaused(bool b){
    Poco::ScopedLock<ofMutex> lock();
    if(currentVideoID != VIDEO_NONE && loadVideoID == VIDEO_NONE){
        bPaused[currentVideoID] = b;
    }
    bPaused[getNextLoadID()] = b;
}
//--------------------------------------------------------------
void ofxThreadedVideo::setFrame(int frame){
    Poco::ScopedLock<ofMutex> lock();
    if(currentVideoID != VIDEO_NONE && loadVideoID == VIDEO_NONE){
        newFrame[currentVideoID] = frame;
    }
    newFrame[getNextLoadID()] = frame;
}
//--------------------------------------------------------------
void ofxThreadedVideo::setSpeed(float speed){
    Poco::ScopedLock<ofMutex> lock();
    if(currentVideoID != VIDEO_NONE && loadVideoID == VIDEO_NONE){
        newSpeed[currentVideoID] = speed;
    }
    newSpeed[getNextLoadID()] = speed;
}
//--------------------------------------------------------------
void ofxThreadedVideo::setLoopState(ofLoopType state){
    Poco::ScopedLock<ofMutex> lock();
    if(currentVideoID != VIDEO_NONE && loadVideoID == VIDEO_NONE){
        newLoopType[currentVideoID] = state;
    }
    newLoopType[getNextLoadID()] = state;
}
//--------------------------------------------------------------
void ofxThreadedVideo::setPosition(float pct){
    Poco::ScopedLock<ofMutex> lock();
    CLAMP(pct, 0.0f, 1.0f);
    if(currentVideoID != VIDEO_NONE && loadVideoID == VIDEO_NONE){
        newPosition[currentVideoID] = pct;
    }
    newPosition[getNextLoadID()] = pct;
}
//--------------------------------------------------------------
void ofxThreadedVideo::update(){
    
    lock();
    
    if(!bCriticalSection && bLoaded){
        bCriticalSection = true;
        int videoID = currentVideoID;
        bool bUpdate = bLoaded;
        unlock();
        
        if(bUpdate){
            
//            lock();
            video[videoID].update();
            
            bIsFrameNew = video[videoID].isFrameNew();
            position = video[videoID].getPosition();
            frameCurrent = video[videoID].getCurrentFrame();
            bIsMovieDone = video[videoID].getIsMovieDone();
//            unlock();
            
            if(bIsFrameNew || bForceFrameNew){
                
                if(bForceFrameNew) lock();
                
                if(!bIsTextureReady) bIsTextureReady = true;
                
                if(drawTexture.getWidth() != width || drawTexture.getHeight() != height){
                    drawTexture.allocate(width, height, ofGetGLTypeFromPixelFormat(video[videoID].getPixelFormat()));
                }
                
                unsigned char * pixels = video[videoID].getPixels();
                if(pixels != NULL && bUseTexture) drawTexture.loadData(pixels, width, height, ofGetGLTypeFromPixelFormat(video[videoID].getPixelFormat()));
                
                if(bForceFrameNew){
                    bForceFrameNew = false;
                    unlock();
                }
                
                // calculate frameRate -> taken from ofAppRunner
                prevMillis = ofGetElapsedTimeMillis();
                timeNow = ofGetElapsedTimef();
                double diff = timeNow-timeThen;
                if( diff  > 0.00001 ){
                    fps			= 1.0 / diff;
                    frameRate	*= 0.9f;
                    frameRate	+= 0.1f*fps;
                }
                lastFrameTime	= diff;
                timeThen		= timeNow;
                
            }
        }
        
        lock();
        bCriticalSection = false;
        unlock();
    }else{
        unlock();
    }
    
    lock();
    ofxThreadedVideoGlobalMutex.lock();
    if(!ofxThreadedVideoGlobalCritical && !bCriticalSection){
        int videoID = currentVideoID;
        ofxThreadedVideoGlobalCritical = true;
        bCriticalSection = true;
        ofxThreadedVideoCommand c = getCommand();
        bool bCanStop = (bLoaded && !bIsLoading) || (!bLoaded && !bIsLoading);
        bool bPopCommand = false;
        unlock();
        ofxThreadedVideoGlobalMutex.unlock();

        if(c.getInstance() == instanceID){
            
            if(c.getCommand() == "stop" && bCanStop){
                if(bVerbose) ofLogVerbose() << instanceID << " = " << c.getCommandAsString();
                if(bIsPlaying) video[videoID].stop();
                lock();
                //fade = 1.0;
                fades.clear();
                bIsPlaying = false;
                bIsPaused = false; // ????
                bIsLoading = false;
                bIsFrameNew = false;
                bIsMovieDone = false;
                bLoaded = false;
                unlock();
                bPopCommand = true;
            }
            
            if(c.getCommand() == "loadMovie" && bCanStop){
                if(bVerbose) ofLogVerbose() << instanceID << " = " << c.getCommandAsString() << " execute in update";
                if(bIsPlaying) video[videoID].stop();
                lock();
                currentVideoID = getNextLoadID();
                bIsPaused = false;
                bLoaded = false;
                bIsLoading = true;
                bIsPlaying = false;
                bIsMovieDone = false;
                unlock();
            }
            
            if(c.getCommand() == "setSpeed"){
                if(bVerbose) ofLogVerbose() << instanceID << " = " << c.getCommandAsString();
                lock();
                speed = c.getArgument<float>(0);
                unlock();
                video[videoID].setSpeed(speed);
                bPopCommand = true;
            }
            
            if(c.getCommand() == "setFrame"){
                if(bVerbose) ofLogVerbose() << instanceID << " = " << c.getCommandAsString();
                lock();
                int frameTarget = c.getArgument<int>(0);
                bForceFrameNew = true;
                frameTarget = CLAMP(frameTarget, 0, frameTotal);
                //cout << "setframe A: " << frameTarget << " " << videoID << " " << bCriticalSection << endl;
                video[videoID].setFrame(frameTarget);
                //cout << "setframe B: " << frameTarget << " " << videoID << " " << bCriticalSection << endl;
                unlock();
                bPopCommand = true;
            }
            
        }
        
        lock();
        ofxThreadedVideoGlobalMutex.lock();
        
        if(bPopCommand) popCommand();
        
        ofxThreadedVideoGlobalCritical = false;
        bCriticalSection = false;
        ofxThreadedVideoGlobalMutex.unlock();
        unlock();
    }else{
        ofxThreadedVideoGlobalMutex.unlock();
        unlock();
    }
}
//--------------------------------------------------------------
void ofxThreadedVideo::threadedFunction(){

    while (isThreadRunning()){

        if(lock()){

            // if there's a movie to load...
            if(loadPath != ""){

                loadVideoID = getNextLoadID();

                paths[loadVideoID] = loadPath;
                loadPath = "";
#ifdef TARGET_OSX
                vector<string> pathParts = ofSplitString(paths[loadVideoID], "/");
#else
                vector<string> pathParts = ofSplitString(paths[loadVideoID], "\\");
#endif
                names[loadVideoID] = pathParts[pathParts.size() - 1];

                // using a static mutex blocks all threads (including the main app) until we've loaded
                ofxThreadedVideoMutex.lock();

                // load that movie!
                if(videos[loadVideoID].loadMovie(paths[loadVideoID])){

                    ofLogVerbose() << "Loaded" << names[loadVideoID] << " " << loadVideoID;

                    // start rolling if AutoPlay is true
                    if (bUseAutoPlay) videos[loadVideoID].play();

                    // set pixel refs
                    pixels[loadVideoID] = &videos[loadVideoID].getPixelsRef();

                }else{

                    ofLogVerbose() << "Could not load video";
                    loadVideoID = VIDEO_NONE;

                    // send event notification
                    ofxThreadedVideoEvent videoEvent = ofxThreadedVideoEvent(paths[loadVideoID], VIDEO_EVENT_LOAD_FAIL, this);
                    ofNotifyEvent(threadedVideoEvent, videoEvent, this);
                }


                ofxThreadedVideoMutex.unlock();

            }

            // do threaded update of videos
            updateVideo(currentVideoID);
            updateVideo(loadVideoID);

            unlock();

            // sleep a bit so we don't thrash the cores!!
            ofSleepMillis(1000/30); // TODO: implement target frame rate? US might need 30 here?

        }
    }
}