//
//  state.hpp
//  aho-corasick
//
//  Created by chenguanglv on 15/12/19.
//  Copyright © 2015年 ibrain. All rights reserved.
//

#ifndef state_h
#define state_h

#include "typedef.hpp"
#include "utf8_len_table.hpp"
#include "emit.hpp"

namespace ac
{
    template<typename T,typename D>
    class State
    {
    public:
        typedef typename ACDef<T>::ACData EmitACData;
        typedef State<T,D>* StatePtr;
        typedef std::set<EmitResult<T, D>> EmitDataSet;
        typedef std::map<T,std::unique_ptr<State<T,D>>> SuccessMap;
        typedef std::vector<StatePtr> StateVector;
        typedef std::vector<T> VariableVector;
    public:
        State() : State(0){}
        
        State(size_t depth)
        : _depth(depth)
        , _root(depth == 0 ? this : nullptr)
        , _failure(nullptr){}
        
        ~State(){}
        
        StatePtr nextState(T var) const
        {
            return nextState(var,false);
        }
        
        StatePtr nextStateIgnoreRoot(T var) const
        {
            return nextState(var,true);
        }
        
        StatePtr addState(T variable)
        {
            auto next = nextStateIgnoreRoot(variable);
            if (!next)
            {
                next = new State(_depth + 1);
                _success[variable].reset(next);
            }
            
            return next;
        }
        
        size_t getDepth() const
        {
            return _depth;
        }
        
        void addEmit(EmitACData& data,D *userdata)
        {
            EmitResult<T,D> emitResult;
#ifdef USE_UTF8_TABLE
            size_t count = 0;
            for (size_t i = 0,bytes = 0; i < data.size(); i += bytes)
            {
                bytes = UTF8_BYTE_LEN((unsigned char)data[i]);
                count++;
            }
            
            emitResult.setCharacterCount(count);
#endif
            emitResult.setEmitResult(data);
            emitResult.setUserData(userdata);
            _emits.insert(emitResult);
        }
        
        void addEmit(const EmitDataSet& emits)
        {
            _emits.insert(emits.cbegin(),emits.cend());
        }
        
        EmitDataSet getEmits() const
        {
            return _emits;
        }
        
        StatePtr failure() const
        {
            return _failure;
        }
        
        void setFailure(StatePtr failure)
        {
            _failure = failure;
        }
        
        StateVector getStates() const
        {
            StateVector states;
            for (auto it = _success.cbegin(); it != _success.cend(); ++it)
            {
                states.push_back(it->second.get());
            }
            
            return states;
        }
        
        VariableVector getVariables() const
        {
            VariableVector vars;
            for (auto it = _success.cbegin(); it != _success.cend(); ++it)
            {
                vars.push_back(it->first);
            }
            return vars;
        }
        
    private:
        StatePtr nextState(T variable, bool ignoreRoot ) const
        {
            StatePtr result = nullptr;
            auto it = _success.find(variable);
            if(it != _success.end())
            {
                result = it->second.get();
            }
            else if(!ignoreRoot && _root)
            {
                result = _root;
            }
            
            return result;
        }
        
    private:
        size_t _depth;
        StatePtr _root;
        SuccessMap _success;
        StatePtr _failure;
        EmitDataSet _emits;
    };
}


#endif /* state_h */
