#include "NascentCommandStream.h"
#include "../RenderCore/Assets/RawAnimationCurve.h"
#include "../Assets/BlockSerializer.h"
#include "../ConsoleRig/OutputStream.h"
#include "../ConsoleRig/Log.h"
#include "../Utility/MemoryUtils.h"
#include "../Utility/StringUtils.h"
#include "../Utility/IteratorUtils.h"

namespace RenderCore { namespace ColladaConversion
{
    using ::Assets::Exceptions::FormatError;

    class NascentAnimationSet::Animation
    {
    public:
        std::string     _name;
        unsigned        _begin, _end;
        unsigned        _constantBegin, _constantEnd;
        float           _startTime, _endTime;
        Animation() : _begin(0), _end(0), _constantBegin(0), _constantEnd(0), _startTime(0.f), _endTime(0.f) {}
        Animation(const std::string& name, unsigned begin, unsigned end, unsigned constantBegin, unsigned constantEnd, float startTime, float endTime)
            : _name(name), _begin(begin), _end(end), _constantBegin(constantBegin), _constantEnd(constantEnd), _startTime(startTime), _endTime(endTime) {}
    };

    AnimSamplerType      SamplerWidthToType(unsigned samplerWidth)
    {
        Assets::TransformationParameterSet::Type::Enum samplerType = Assets::TransformationParameterSet::Type::Float1;
        switch (samplerWidth) {
        case  1:    samplerType = Assets::TransformationParameterSet::Type::Float1;     break;
        case  3:    samplerType = Assets::TransformationParameterSet::Type::Float3;     break;
        case  4:    samplerType = Assets::TransformationParameterSet::Type::Float4;     break;
        case 16:    samplerType = Assets::TransformationParameterSet::Type::Float4x4;   break;
        default:
            Throw(
                FormatError(
                    "Strange sampler width encountered when adding animation driver!"));
        }

        return samplerType;
    }

    size_t      SamplerSize(AnimSamplerType samplerType)
    {
        switch (samplerType) {
        case Assets::TransformationParameterSet::Type::Float1:      return sizeof(float);
        case Assets::TransformationParameterSet::Type::Float3:      return sizeof(Float3);
        case Assets::TransformationParameterSet::Type::Float4:      return sizeof(Float4);
        case Assets::TransformationParameterSet::Type::Float4x4:    return sizeof(Float4x4);
        }
        return 0;
    }

    void    NascentAnimationSet::AddConstantDriver(
                                    const std::string&  parameterName,
                                    const void*         constantValue,
                                    AnimSamplerType     samplerType,
                                    unsigned            samplerOffset)
    {
        size_t parameterIndex = _parameterInterfaceDefinition.size();
        auto i = std::find( _parameterInterfaceDefinition.cbegin(),
                            _parameterInterfaceDefinition.cend(), parameterName);
        if (i!=_parameterInterfaceDefinition.end()) {
            parameterIndex = (unsigned)std::distance(_parameterInterfaceDefinition.cbegin(), i);
        } else {
            _parameterInterfaceDefinition.push_back(parameterName);
        }

        unsigned dataOffset = unsigned(_constantData.size());
        std::copy(
            (uint8*)constantValue, PtrAdd((uint8*)constantValue, SamplerSize(samplerType)),
            std::back_inserter(_constantData));

        _constantDrivers.push_back(
            ConstantDriver(dataOffset, (unsigned)parameterIndex, samplerType, samplerOffset));
    }

    void    NascentAnimationSet::AddAnimationDriver(
        const std::string& parameterName,
        unsigned curveId,
        AnimSamplerType samplerType, unsigned samplerOffset)
    {
        size_t parameterIndex = _parameterInterfaceDefinition.size();
        auto i = std::find( _parameterInterfaceDefinition.cbegin(),
                            _parameterInterfaceDefinition.cend(), parameterName);
        if (i!=_parameterInterfaceDefinition.end()) {
            parameterIndex = (unsigned)std::distance(_parameterInterfaceDefinition.cbegin(), i);
        } else {
            _parameterInterfaceDefinition.push_back(parameterName);
        }

        _animationDrivers.push_back(
            AnimationDriver(curveId, (unsigned)parameterIndex, samplerType, samplerOffset));
    }

    bool    NascentAnimationSet::HasAnimationDriver(const std::string&  parameterName) const
    {
        auto i = std::find( _parameterInterfaceDefinition.cbegin(),
                            _parameterInterfaceDefinition.cend(), parameterName);
        if (i==_parameterInterfaceDefinition.end())
            return false;

        auto parameterIndex = (unsigned)std::distance(_parameterInterfaceDefinition.cbegin(), i);

        for (auto i=_animationDrivers.begin(); i!=_animationDrivers.end(); ++i) {
            if (i->_parameterIndex == parameterIndex)
                return true;
        }

        for (auto i=_constantDrivers.begin(); i!=_constantDrivers.end(); ++i) {
            if (i->_parameterIndex == parameterIndex)
                return true;
        }
        return false;
    }

    void    NascentAnimationSet::MergeAnimation(
        const NascentAnimationSet& animation, const char name[],
        const std::vector<Assets::RawAnimationCurve>& sourceCurves,
        std::vector<Assets::RawAnimationCurve>& destinationCurves)
    {
            //      Merge the animation drivers in the given input animation, and give
            //      them the supplied name
        float minTime = FLT_MAX, maxTime = -FLT_MAX;
        size_t startIndex = _animationDrivers.size();
        size_t constantStartIndex = _constantDrivers.size();
        for (auto i=animation._animationDrivers.cbegin(); i!=animation._animationDrivers.end(); ++i) {
            if (i->_curveIndex >= sourceCurves.size()) continue;
            const auto* animCurve = &sourceCurves[i->_curveIndex];
            if (animCurve) {
                float curveStart = animCurve->StartTime();
                float curveEnd = animCurve->EndTime();
                minTime = std::min(minTime, curveStart);
                maxTime = std::max(maxTime, curveEnd);

                const std::string& name = animation._parameterInterfaceDefinition[i->_parameterIndex];
                destinationCurves.push_back(Assets::RawAnimationCurve(*animCurve));
                AddAnimationDriver(
                    name, unsigned(destinationCurves.size()-1),
                    i->_samplerType, i->_samplerOffset);
            }
        }

        for (auto i=animation._constantDrivers.cbegin(); i!=animation._constantDrivers.end(); ++i) {
            const std::string& name = animation._parameterInterfaceDefinition[i->_parameterIndex];
            AddConstantDriver(name, PtrAdd(AsPointer(animation._constantData.begin()), i->_dataOffset), i->_samplerType, i->_samplerOffset);
        }

        _animations.push_back(Animation(name,
            (unsigned)startIndex, (unsigned)_animationDrivers.size(),
            (unsigned)constantStartIndex, (unsigned)_constantDrivers.size(),
            minTime, maxTime));
    }

    void NascentAnimationSet::AnimationDriver::Serialize(Serialization::NascentBlockSerializer& serializer) const
    {
        ::Serialize(serializer, unsigned(_curveIndex));
        ::Serialize(serializer, _parameterIndex);
        ::Serialize(serializer, _samplerOffset);
        ::Serialize(serializer, unsigned(_samplerType));
    }

    void NascentAnimationSet::ConstantDriver::Serialize(Serialization::NascentBlockSerializer& serializer) const
    {
        ::Serialize(serializer, _dataOffset);
        ::Serialize(serializer, _parameterIndex);
        ::Serialize(serializer, _samplerOffset);
        ::Serialize(serializer, unsigned(_samplerType));
    }

    struct AnimationDesc        // matches AnimationSet::Animation
    {
        uint64      _name;
        unsigned    _beginDriver, _endDriver;
        unsigned    _beginConstantDriver, _endConstantDriver;
        float       _beginTime, _endTime;

        AnimationDesc() {}
        void Serialize(Serialization::NascentBlockSerializer& serializer) const
        {
            ::Serialize(serializer, _name);
            ::Serialize(serializer, _beginDriver);
            ::Serialize(serializer, _endDriver);
            ::Serialize(serializer, _beginConstantDriver);
            ::Serialize(serializer, _endConstantDriver);
            ::Serialize(serializer, _beginTime);
            ::Serialize(serializer, _endTime);
        }
    };

    struct CompareAnimationName
    {
        bool operator()(const AnimationDesc& lhs, const AnimationDesc& rhs) const   { return lhs._name < rhs._name; }
        bool operator()(const AnimationDesc& lhs, uint64 rhs) const                 { return lhs._name < rhs; }
        bool operator()(uint64 lhs, const AnimationDesc& rhs) const                 { return lhs < rhs._name; }
    };

    void NascentAnimationSet::Serialize(Serialization::NascentBlockSerializer& serializer) const
    {
        serializer.SerializeSubBlock(AsPointer(_animationDrivers.begin()), AsPointer(_animationDrivers.end()));
        serializer.SerializeValue(_animationDrivers.size());
        serializer.SerializeSubBlock(AsPointer(_constantDrivers.begin()), AsPointer(_constantDrivers.end()));
        serializer.SerializeValue(_constantDrivers.size());
        serializer.SerializeSubBlock(AsPointer(_constantData.begin()), AsPointer(_constantData.end()));

            //      List of animations...

        auto outputAnimations = std::make_unique<AnimationDesc[]>(_animations.size());
        for (size_t c=0; c<_animations.size(); ++c) {
            AnimationDesc&o = outputAnimations[c];
            const Animation&i = _animations[c];
            o._name = Hash64(AsPointer(i._name.begin()), AsPointer(i._name.end()));
            o._beginDriver = i._begin; o._endDriver = i._end;
            o._beginConstantDriver = i._constantBegin; o._endConstantDriver = i._constantEnd;
            o._beginTime = i._startTime; o._endTime = i._endTime;
        }
        std::sort(outputAnimations.get(), &outputAnimations[_animations.size()], CompareAnimationName());
        serializer.SerializeSubBlock(outputAnimations.get(), &outputAnimations[_animations.size()]);
        serializer.SerializeValue(_animations.size());

        ConsoleRig::DebuggerOnlyWarning("Animation set output interface:\n");
        auto parameterNameHashes = std::make_unique<uint64[]>(_parameterInterfaceDefinition.size());
        for (size_t c=0; c<_parameterInterfaceDefinition.size(); ++c) {
            ConsoleRig::DebuggerOnlyWarning("  [%i] %s\n", c, _parameterInterfaceDefinition[c].c_str());
            parameterNameHashes[c] = Hash64(AsPointer(_parameterInterfaceDefinition[c].begin()), AsPointer(_parameterInterfaceDefinition[c].end()));
        }
        serializer.SerializeSubBlock(parameterNameHashes.get(), &parameterNameHashes[_parameterInterfaceDefinition.size()]);
        serializer.SerializeValue(_parameterInterfaceDefinition.size());
    }

    NascentAnimationSet::NascentAnimationSet() {}
    NascentAnimationSet::~NascentAnimationSet() {}
    NascentAnimationSet::NascentAnimationSet(NascentAnimationSet&& moveFrom)
    :   _animationDrivers(std::move(moveFrom._animationDrivers))
    ,   _constantDrivers(std::move(moveFrom._constantDrivers))
    ,   _animations(std::move(moveFrom._animations))
    ,   _parameterInterfaceDefinition(std::move(moveFrom._parameterInterfaceDefinition))
    ,   _constantData(std::move(moveFrom._constantData))
    {}
    NascentAnimationSet& NascentAnimationSet::operator=(NascentAnimationSet&& moveFrom)
    {
        _animationDrivers = std::move(moveFrom._animationDrivers);
        _constantDrivers = std::move(moveFrom._constantDrivers);
        _animations = std::move(moveFrom._animations);
        _parameterInterfaceDefinition = std::move(moveFrom._parameterInterfaceDefinition);
        _constantData = std::move(moveFrom._constantData);
        return *this;
    }

    void NascentSkeleton::Serialize(Serialization::NascentBlockSerializer& serializer) const
    {
        ::Serialize(serializer, _transformationMachine);
    }

    NascentSkeleton::NascentSkeleton() {}
    NascentSkeleton::~NascentSkeleton() {}
    NascentSkeleton::NascentSkeleton(NascentSkeleton&& moveFrom)
    :   _transformationMachine(std::move(moveFrom._transformationMachine))
    {}
    NascentSkeleton& NascentSkeleton::operator=(NascentSkeleton&& moveFrom)
    {
        _transformationMachine = std::move(moveFrom._transformationMachine);
        return *this;
    }

    class NascentModelCommandStream::TransformationMachineOutput
    {
    public:
        ObjectGuid _colladaId;
        std::string _name;
    };

    void NascentModelCommandStream::RegisterTransformationMachineOutput(const std::string& bindingName, ObjectGuid id, unsigned transformMarker)
    {
        if (_transformationMachineOutputs.size() < transformMarker+1)
            _transformationMachineOutputs.resize(transformMarker+1);
        _transformationMachineOutputs[transformMarker] = TransformationMachineOutput{id, bindingName};
    }

    unsigned    NascentModelCommandStream::FindTransformationMachineOutput(ObjectGuid nodeId) const
    {
        auto i2=_transformationMachineOutputs.cbegin();
        for (;i2!=_transformationMachineOutputs.cend(); ++i2) {
            if (i2->_colladaId == nodeId) {
                return (unsigned)std::distance(_transformationMachineOutputs.cbegin(), i2);
            }
        }
        return ~unsigned(0x0);
    }

    void NascentModelCommandStream::Add(GeometryInstance&& geoInstance)
    {
        _geometryInstances.emplace_back(std::move(geoInstance));
    }

    void NascentModelCommandStream::Add(CameraInstance&& camInstance)
    {
        _cameraInstances.emplace_back(std::move(camInstance));
    }

    void NascentModelCommandStream::Add(SkinControllerInstance&& skinControllerInstance)
    {
        _skinControllerInstances.emplace_back(std::move(skinControllerInstance));
    }

    NascentModelCommandStream::NascentModelCommandStream()
    {
    }

    NascentModelCommandStream::~NascentModelCommandStream()
    {

    }

    NascentModelCommandStream::NascentModelCommandStream(NascentModelCommandStream&& moveFrom)
    :       _transformationMachineOutputs(std::move(moveFrom._transformationMachineOutputs))
    ,       _geometryInstances(std::move(moveFrom._geometryInstances))
    ,       _cameraInstances(std::move(moveFrom._cameraInstances))
    ,       _skinControllerInstances(std::move(moveFrom._skinControllerInstances))
    {
    }

    NascentModelCommandStream& NascentModelCommandStream::operator=(NascentModelCommandStream&& moveFrom) never_throws
    {
        _transformationMachineOutputs = std::move(moveFrom._transformationMachineOutputs);
        _geometryInstances = std::move(moveFrom._geometryInstances);
        _cameraInstances = std::move(moveFrom._cameraInstances);
        _skinControllerInstances = std::move(moveFrom._skinControllerInstances);
        return *this;
    }

    void NascentModelCommandStream::GeometryInstance::Serialize(Serialization::NascentBlockSerializer& serializer) const
    {
        ::Serialize(serializer, _id);
        ::Serialize(serializer, _localToWorldId);
        serializer.SerializeSubBlock(AsPointer(_materials.begin())

        ::Serialize(serializer, _materials.size());
        ::Serialize(serializer, _levelOfDetail);
    }

    void NascentModelCommandStream::SkinControllerInstance::Serialize(Serialization::NascentBlockSerializer& serializer) const
    {
        ::Serialize(serializer, _id);
        ::Serialize(serializer, _localToWorldId);
        serializer.SerializeSubBlock(AsPointer(_materials.begin()), AsPointer(_materials.end()));
        ::Serialize(serializer, _materials.size());
        ::Serialize(serializer, _levelOfDetail);
    }

    void NascentModelCommandStream::Serialize(Serialization::NascentBlockSerializer& serializer) const
    {
        serializer.SerializeSubBlock(AsPointer(_geometryInstances.begin()), AsPointer(_geometryInstances.end()));
        serializer.SerializeValue(_geometryInstances.size());
        serializer.SerializeSubBlock(AsPointer(_skinControllerInstances.begin()), AsPointer(_skinControllerInstances.end()));
        serializer.SerializeValue(_skinControllerInstances.size());

            //      Turn our list of input matrices into hash values, and write out the
        ConsoleRig::DebuggerOnlyWarning("Command stream input interface:\n");
        auto inputInterface = GetInputInterface();
        serializer.SerializeSubBlock(AsPointer(inputInterface.cbegin()), AsPointer(inputInterface.cend()));
        serializer.SerializeValue(_transformationMachineOutputs.size());
    }

    std::vector<uint64> NascentModelCommandStream::GetInputInterface() const
    {
        std::vector<uint64> inputInterface(_transformationMachineOutputs.size());
        unsigned c=0;
        for (auto i=_transformationMachineOutputs.begin(); i!=_transformationMachineOutputs.end(); ++i, ++c)
            inputInterface[c] = Hash64(AsPointer(i->_name.begin()), AsPointer(i->_name.end()));
        return std::move(inputInterface);
    }

    unsigned NascentModelCommandStream::GetMaxLOD() const
    {
        unsigned maxLOD = 0u;
        for (const auto&i:_geometryInstances) maxLOD = std::max(i._levelOfDetail, maxLOD);
        for (const auto&i:_skinControllerInstances) maxLOD = std::max(i._levelOfDetail, maxLOD);
        return maxLOD;
    }

    std::ostream& operator<<(std::ostream& stream, const NascentModelCommandStream& cmdStream)
    {
        stream << " --- Geometry instances:" << std::endl;
        unsigned c=0;
        for (const auto& i:cmdStream._geometryInstances) {
            stream << "  [" << c++ << "] GeoId: " << i._id << " Transform: " << i._localToWorldId << " LOD: " << i._levelOfDetail << std::endl;
            stream << "     Materials: " << std::hex;
            for (size_t q=0; q<i._materials.size(); ++q) {
                if (q != 0) stream << ", ";
                stream << i._materials[q];
            }
            stream << std::dec << std::endl;
        }

        stream << " --- Skin controller instances:" << std::endl;
        c=0;
        for (const auto& i:cmdStream._skinControllerInstances) {
            stream << "  [" << c++ << "] GeoId: " << i._id << " Transform: " << i._localToWorldId << std::endl;
            stream << "     Materials: " << std::hex;
            for (size_t q=0; q<i._materials.size(); ++q) {
                if (q != 0) stream << ", ";
                stream << i._materials[q];
            }
            stream << std::dec << std::endl;
        }

        stream << " --- Camera instances:" << std::endl;
        c=0;
        for (const auto& i:cmdStream._cameraInstances)
            stream << "  [" << c++ << "] Transform: " << i._localToWorldId << std::endl;

        stream << " --- Input interface:" << std::endl;
        c=0;
        for (const auto& i:cmdStream._transformationMachineOutputs)
            stream << "  [" << c++ << "] " << i._name << std::endl;
        return stream;
    }

}}


switch (samplerType) {
        case Assets::TransformationParameterSet::Type::Float1:      return sizeof(float);
        case Assets::TransformationParameterSet::Type::Float3:      return sizeof(Float3);
        case Assets::TransformationParameterSet::Type::Float4:      return sizeof(Float4);
        case Assets::TransformationParameterSet::Type::Float4x4:    return sizeof(Float4x4);
        }

#include "NascentCommandStream.h"
#include "../RenderCore/Assets/RawAnimationCurve.h"
#include "../Assets/BlockSerializer.h"
#include "../ConsoleRig/OutputStream.h"
#include "../ConsoleRig/Log.h"
#include "../Utility/MemoryUtils.h"
#include "../Utility/StringUtils.h"
#include "../Utility/IteratorUtils.h"

namespace RenderCore { namespace ColladaConversion
{
    using ::Assets::Exceptions::FormatError;

    class NascentAnimationSet::Animation
    {
    public:
        std::string     _name;
        unsigned        _begin, _end;
        unsigned        _constantBegin, _constantEnd;
        float           _startTime, _endTime;
        Animation() : _begin(0), _end(0), _constantBegin(0), _constantEnd(0), _startTime(0.f), _endTime(0.f) {}
        Animation(const std::string& name, unsigned begin, unsigned end, unsigned constantBegin, unsigned constantEnd, float startTime, float endTime)
            : _name(name), _begin(begin), _end(end), _constantBegin(constantBegin), _constantEnd(constantEnd), _startTime(startTime), _endTime(endTime) {}
    };

    AnimSamplerType      SamplerWidthToType(unsigned samplerWidth)
    {
        Assets::TransformationParameterSet::Type::Enum samplerType = Assets::TransformationParameterSet::Type::Float1;
        switch (samplerWidth) {
        case  1:    samplerType = Assets::TransformationParameterSet::Type::Float1;     break;
        case  3:    samplerType = Assets::TransformationParameterSet::Type::Float3;     break;
        case  4:    samplerType = Assets::TransformationParameterSet::Type::Float4;     break;
        case 16:    samplerType = Assets::TransformationParameterSet::Type::Float4x4;   break;
        default:
            Throw(
                FormatError(
                    "Strange sampler width encountered when adding animation driver!"));
        }

        return samplerType;
    }

    size_t      SamplerSize(AnimSamplerType samplerType)
    {

        return 0;
    }

    void    NascentAnimationSet::AddConstantDriver(
                                    const std::string&  parameterName,
                                    const void*         constantValue,
                                    AnimSamplerType     samplerType,
                                    unsigned            samplerOffset)
    {
        size_t parameterIndex = _parameterInterfaceDefinition.size();
        auto i = std::find( _parameterInterfaceDefinition.cbegin(),
                            _parameterInterfaceDefinition.cend(), parameterName);
        if (i!=_parameterInterfaceDefinition.end()) {
            parameterIndex = (unsigned)std::distance(_parameterInterfaceDefinition.cbegin(), i);
        } else {
            _parameterInterfaceDefinition.push_back(parameterName);
        }

        unsigned dataOffset = unsigned(_constantData.size());
        std::copy(
            (uint8*)constantValue, PtrAdd((uint8*)constantValue, SamplerSize(samplerType)),
            std::back_inserter(_constantData));

        _constantDrivers.push_back(
            ConstantDriver(dataOffset, (unsigned)parameterIndex, samplerType, samplerOffset));
    }

    void    NascentAnimationSet::AddAnimationDriver(
        const std::string& parameterName,
        unsigned curveId,
        AnimSamplerType samplerType, unsigned samplerOffset)
    {
        size_t parameterIndex = _parameterInterfaceDefinition.size();
        auto i = std::find( _parameterInterfaceDefinition.cbegin(),
                            _parameterInterfaceDefinition.cend(), parameterName);
        if (i!=_parameterInterfaceDefinition.end()) {
            parameterIndex = (unsigned)std::distance(_parameterInterfaceDefinition.cbegin(), i);
        } else {
            _parameterInterfaceDefinition.push_back(parameterName);
        }

        _animationDrivers.push_back(
            AnimationDriver(curveId, (unsigned)parameterIndex, samplerType, samplerOffset));
    }

    bool    NascentAnimationSet::HasAnimationDriver(const std::string&  parameterName) const
    {
        auto i = std::find( _parameterInterfaceDefinition.cbegin(),
                            _parameterInterfaceDefinition.cend(), parameterName);
        if (i==_parameterInterfaceDefinition.end())
            return false;

        auto parameterIndex = (unsigned)std::distance(_parameterInterfaceDefinition.cbegin(), i);

        for (auto i=_animationDrivers.begin(); i!=_animationDrivers.end(); ++i) {
            if (i->_parameterIndex == parameterIndex)
                return true;
        }

        for (auto i=_constantDrivers.begin(); i!=_constantDrivers.end(); ++i) {
            if (i->_parameterIndex == parameterIndex)
                return true;
        }
        return false;
    }

    void    NascentAnimationSet::MergeAnimation(
        const NascentAnimationSet& animation, const char name[],
        const std::vector<Assets::RawAnimationCurve>& sourceCurves,
        std::vector<Assets::RawAnimationCurve>& destinationCurves)
    {
            //      Merge the animation drivers in the given input animation, and give
            //      them the supplied name
        float minTime = FLT_MAX, maxTime = -FLT_MAX;
        size_t startIndex = _animationDrivers.size();
        size_t constantStartIndex = _constantDrivers.size();
        for (auto i=animation._animationDrivers.cbegin(); i!=animation._animationDrivers.end(); ++i) {
            if (i->_curveIndex >= sourceCurves.size()) continue;
            const auto* animCurve = &sourceCurves[i->_curveIndex];
            if (animCurve) {
                float curveStart = animCurve->StartTime();
                float curveEnd = animCurve->EndTime();
                minTime = std::min(minTime, curveStart);
                maxTime = std::max(maxTime, curveEnd);

                const std::string& name = animation._parameterInterfaceDefinition[i->_parameterIndex];
                destinationCurves.push_back(Assets::RawAnimationCurve(*animCurve));
                AddAnimationDriver(
                    name, unsigned(destinationCurves.size()-1),
                    i->_samplerType, i->_samplerOffset);
            }
        }

        for (auto i=animation._constantDrivers.cbegin(); i!=animation._constantDrivers.end(); ++i) {
            const std::string& name = animation._parameterInterfaceDefinition[i->_parameterIndex];
            AddConstantDriver(name, PtrAdd(AsPointer(animation._constantData.begin()), i->_dataOffset), i->_samplerType, i->_samplerOffset);
        }

        _animations.push_back(Animation(name,
            (unsigned)startIndex, (unsigned)_animationDrivers.size(),
            (unsigned)constantStartIndex, (unsigned)_constantDrivers.size(),
            minTime, maxTime));
    }

    void NascentAnimationSet::AnimationDriver::Serialize(Serialization::NascentBlockSerializer& serializer) const
    {
        ::Serialize(serializer, unsigned(_curveIndex));
        ::Serialize(serializer, _parameterIndex);
        ::Serialize(serializer, _samplerOffset);
        ::Serialize(serializer, unsigned(_samplerType));
    }

    void NascentAnimationSet::ConstantDriver::Serialize(Serialization::NascentBlockSerializer& serializer) const
    {
        ::Serialize(serializer, _dataOffset);
        ::Serialize(serializer, _parameterIndex);
        ::Serialize(serializer, _samplerOffset);
        ::Serialize(serializer, unsigned(_samplerType));
    }

    struct AnimationDesc        // matches AnimationSet::Animation
    {
        uint64      _name;
        unsigned    _beginDriver, _endDriver;
        unsigned    _beginConstantDriver, _endConstantDriver;
        float       _beginTime, _endTime;

        AnimationDesc() {}
        void Serialize(Serialization::NascentBlockSerializer& serializer) const
        {
            ::Serialize(serializer, _name);
            ::Serialize(serializer, _beginDriver);
            ::Serialize(serializer, _endDriver);
            ::Serialize(serializer, _beginConstantDriver);
            ::Serialize(serializer, _endConstantDriver);
            ::Serialize(serializer, _beginTime);
            ::Serialize(serializer, _endTime);
        }
    };

    struct CompareAnimationName
    {
        bool operator()(const AnimationDesc& lhs, const AnimationDesc& rhs) const   { return lhs._name < rhs._name; }
        bool operator()(const AnimationDesc& lhs, uint64 rhs) const                 { return lhs._name < rhs; }
        bool operator()(uint64 lhs, const AnimationDesc& rhs) const                 { return lhs < rhs._name; }
    };

    void NascentAnimationSet::Serialize(Serialization::NascentBlockSerializer& serializer) const
    {
        serializer.SerializeSubBlock(AsPointer(_animationDrivers.begin()), AsPointer(_animationDrivers.end()));
        serializer.SerializeValue(_animationDrivers.size());
        serializer.SerializeSubBlock(AsPointer(_constantDrivers.begin()), AsPointer(_constantDrivers.end()));
        serializer.SerializeValue(_constantDrivers.size());
        serializer.SerializeSubBlock(AsPointer(_constantData.begin()), AsPointer(_constantData.end()));

            //      List of animations...

        auto outputAnimations = std::make_unique<AnimationDesc[]>(_animations.size());
        for (size_t c=0; c<_animations.size(); ++c) {
            AnimationDesc&o = outputAnimations[c];
            const Animation&i = _animations[c];
            o._name = Hash64(AsPointer(i._name.begin()), AsPointer(i._name.end()));
            o._beginDriver = i._begin; o._endDriver = i._end;
            o._beginConstantDriver = i._constantBegin; o._endConstantDriver = i._constantEnd;
            o._beginTime = i._startTime; o._endTime = i._endTime;
        }
        std::sort(outputAnimations.get(), &outputAnimations[_animations.size()], CompareAnimationName());
        serializer.SerializeSubBlock(outputAnimations.get(), &outputAnimations[_animations.size()]);
        serializer.SerializeValue(_animations.size());

        ConsoleRig::DebuggerOnlyWarning("Animation set output interface:\n");
        auto parameterNameHashes = std::make_unique<uint64[]>(_parameterInterfaceDefinition.size());
        for (size_t c=0; c<_parameterInterfaceDefinition.size(); ++c) {
            ConsoleRig::DebuggerOnlyWarning("  [%i] %s\n", c, _parameterInterfaceDefinition[c].c_str());
            parameterNameHashes[c] = Hash64(AsPointer(_parameterInterfaceDefinition[c].begin()), AsPointer(_parameterInterfaceDefinition[c].end()));
        }
        serializer.SerializeSubBlock(parameterNameHashes.get(), &parameterNameHashes[_parameterInterfaceDefinition.size()]);
        serializer.SerializeValue(_parameterInterfaceDefinition.size());
    }

    NascentAnimationSet::NascentAnimationSet() {}
    NascentAnimationSet::~NascentAnimationSet() {}
    NascentAnimationSet::NascentAnimationSet(NascentAnimationSet&& moveFrom)
    :   _animationDrivers(std::move(moveFrom._animationDrivers))
    ,   _constantDrivers(std::move(moveFrom._constantDrivers))
    ,   _animations(std::move(moveFrom._animations))
    ,   _parameterInterfaceDefinition(std::move(moveFrom._parameterInterfaceDefinition))
    ,   _constantData(std::move(moveFrom._constantData))
    {}
    NascentAnimationSet& NascentAnimationSet::operator=(NascentAnimationSet&& moveFrom)
    {
        _animationDrivers = std::move(moveFrom._animationDrivers);
        _constantDrivers = std::move(moveFrom._constantDrivers);
        _animations = std::move(moveFrom._animations);
        _parameterInterfaceDefinition = std::move(moveFrom._parameterInterfaceDefinition);
        _constantData = std::move(moveFrom._constantData);
        return *this;
    }

    void NascentSkeleton::Serialize(Serialization::NascentBlockSerializer& serializer) const
    {
        ::Serialize(serializer, _transformationMachine);
    }

    NascentSkeleton::NascentSkeleton() {}
    NascentSkeleton::~NascentSkeleton() {}
    NascentSkeleton::NascentSkeleton(NascentSkeleton&& moveFrom)
    :   _transformationMachine(std::move(moveFrom._transformationMachine))
    {}
    NascentSkeleton& NascentSkeleton::operator=(NascentSkeleton&& moveFrom)
    {
        _transformationMachine = std::move(moveFrom._transformationMachine);
        return *this;
    }

    class NascentModelCommandStream::TransformationMachineOutput
    {
    public:
        ObjectGuid _colladaId;
        std::string _name;
    };

    void NascentModelCommandStream::RegisterTransformationMachineOutput(const std::string& bindingName, ObjectGuid id, unsigned transformMarker)
    {
        if (_transformationMachineOutputs.size() < transformMarker+1)
            _transformationMachineOutputs.resize(transformMarker+1);
        _transformationMachineOutputs[transformMarker] = TransformationMachineOutput{id, bindingName};
    }

    unsigned    NascentModelCommandStream::FindTransformationMachineOutput(ObjectGuid nodeId) const
    {
        auto i2=_transformationMachineOutputs.cbegin();
        for (;i2!=_transformationMachineOutputs.cend(); ++i2) {
            if (i2->_colladaId == nodeId) {
                return (unsigned)std::distance(_transformationMachineOutputs.cbegin(), i2);
            }
        }
        return ~unsigned(0x0);
    }

    void NascentModelCommandStream::Add(GeometryInstance&& geoInstance)
    {
        _geometryInstances.emplace_back(std::move(geoInstance));
    }

    void NascentModelCommandStream::Add(CameraInstance&& camInstance)
    {
        _cameraInstances.emplace_back(std::move(camInstance));
    }

    void NascentModelCommandStream::Add(SkinControllerInstance&& skinControllerInstance)
    {
        _skinControllerInstances.emplace_back(std::move(skinControllerInstance));
    }

    NascentModelCommandStream::NascentModelCommandStream()
    {
    }

    NascentModelCommandStream::~NascentModelCommandStream()
    {

    }

    NascentModelCommandStream::NascentModelCommandStream(NascentModelCommandStream&& moveFrom)
    :       _transformationMachineOutputs(std::move(moveFrom._transformationMachineOutputs))
    ,       _geometryInstances(std::move(moveFrom._geometryInstances))
    ,       _cameraInstances(std::move(moveFrom._cameraInstances))
    ,       _skinControllerInstances(std::move(moveFrom._skinControllerInstances))
    {
    }

    NascentModelCommandStream& NascentModelCommandStream::operator=(NascentModelCommandStream&& moveFrom) never_throws
    {
        _transformationMachineOutputs = std::move(moveFrom._transformationMachineOutputs);
        _geometryInstances = std::move(moveFrom._geometryInstances);
        _cameraInstances = std::move(moveFrom._cameraInstances);
        _skinControllerInstances = std::move(moveFrom._skinControllerInstances);
        return *this;
    }

    void NascentModelCommandStream::GeometryInstance::Serialize(Serialization::NascentBlockSerializer& serializer) const
    {
        ::Serialize(serializer, _id);
        ::Serialize(serializer, _localToWorldId);
        serializer.SerializeSubBlock(AsPointer(_materials.begin()), AsPointer(_materials.end()));
        ::Serialize(serializer, _materials.size());
        ::Serialize(serializer, _levelOfDetail);
    }

    void NascentModelCommandStream::SkinControllerInstance::Serialize(Serialization::NascentBlockSerializer& serializer) const
    {
        ::Serialize(serializer, _id);
        ::Serialize(serializer, _localToWorldId);
        serializer.SerializeSubBlock(AsPointer(_materials.begin()), AsPointer(_materials.end()));
        ::Serialize(serializer, _materials.size());
        ::Serialize(serializer, _levelOfDetail);
    }

    void NascentModelCommandStream::Serialize(Serialization::NascentBlockSerializer& serializer) const
    {
        serializer.SerializeSubBlock(AsPointer(_geometryInstances.begin()), AsPointer(_geometryInstances.end()));
        serializer.SerializeValue(_geometryInstances.size());
        serializer.SerializeSubBlock(AsPointer(_skinControllerInstances.begin()), AsPointer(_skinControllerInstances.end()));
        serializer.SerializeValue(_skinControllerInstances.size());

            //      Turn our list of input matrices into hash values, and write out the
        ConsoleRig::DebuggerOnlyWarning("Command stream input interface:\n");
        auto inputInterface = GetInputInterface();
        serializer.SerializeSubBlock(AsPointer(inputInterface.cbegin()), AsPointer(inputInterface.cend()));
        serializer.SerializeValue(_transformationMachineOutputs.size());
    }

    std::vector<uint64> NascentModelCommandStream::GetInputInterface() const
    {
        std::vector<uint64> inputInterface(_transformationMachineOutputs.size());
        unsigned c=0;
        for (auto i=_transformationMachineOutputs.begin(); i!=_transformationMachineOutputs.end(); ++i, ++c)
            inputInterface[c] = Hash64(AsPointer(i->_name.begin()), AsPointer(i->_name.end()));
        return std::move(inputInterface);
    }

    unsigned NascentModelCommandStream::GetMaxLOD() const
    {
        unsigned maxLOD = 0u;
        for (const auto&i:_geometryInstances) maxLOD = std::max(i._levelOfDetail, maxLOD);
        for (const auto&i:_skinControllerInstances) maxLOD = std::max(i._levelOfDetail, maxLOD);
        return maxLOD;
    }

    std::ostream& operator<<(std::ostream& stream, const NascentModelCommandStream& cmdStream)
    {
        stream << " --- Geometry instances:" << std::endl;
        unsigned c=0;
        for (const auto& i:cmdStream._geometryInstances) {
            stream << "  [" << c++ << "] GeoId: " << i._id << " Transform: " << i._localToWorldId << " LOD: " << i._levelOfDetail << std::endl;
            stream << "     Materials: " << std::hex;
            for (size_t q=0; q<i._materials.size(); ++q) {
                if (q != 0) stream << ", ";
                stream << i._materials[q];
            }
            stream << std::dec << std::endl;
        }

        stream << " --- Skin controller instances:" << std::endl;
        c=0;
        for (const auto& i:cmdStream._skinControllerInstances) {
            stream << "  [" << c++ << "] GeoId: " << i._id << " Transform: " << i._localToWorldId << std::endl;
            stream << "     Materials: " << std::hex;
            for (size_t q=0; q<i._materials.size(); ++q) {
                if (q != 0) stream << ", ";
                stream << i._materials[q];
            }
            stream << std::dec << std::endl;
        }

        stream << " --- Camera instances:" << std::endl;
        c=0;
        for (const auto& i:cmdStream._cameraInstances)
            stream << "  [" << c++ << "] Transform: " << i._localToWorldId << std::endl;

        stream << " --- Input interface:" << std::endl;
        c=0;
        for (const auto& i:cmdStream._transformationMachineOutputs)
            stream << "  [" << c++ << "] " << i._name << std::endl;
        return stream;
    }

}}


#include <QDomElement>
#include <QDebug>
#include <QFileInfo>

#include "main.h"
#include "Attach.h"
#include "AttachTableData.h"
#include "AttachTableModel.h"
#include "models/recordTable/Record.h"
#include "libraries/helpers/DebugHelper.h"

AttachTableData::AttachTableData(Record *iRecord)
{
  liteFlag=true;
  attachTable.clear();
  record=iRecord;
  relatedAttachTableModel=NULL;
}

/*
AttachTableData::AttachTableData(const AttachTableData &obj)
{
  attachTable=new QList< Attach >(obj.attachTable); // "Глубокое" копирование таблицы прикрепляемых файлов
  record=obj.record; // А ссылка на запись просто копируется
}
*/

AttachTableData::AttachTableData()
{
  liteFlag=true;
  attachTable.clear();
  record=NULL;
  relatedAttachTableModel=NULL;
}

AttachTableData::~AttachTableData()
{

}

void AttachTableData::setupDataFromDom(QDomElement iDomElement)
{
  QDomElement currentFile=iDomElement.firstChildElement("file");

  while(!currentFile.isNull())
  {
    Attach attach(this);

    attachTable.append(attach);

    attachTable.last().setupDataFromDom(currentFile);

    currentFile=currentFile.nextSiblingElement("file");
  }
}

QDomElement AttachTableData::exportDataToDom(QDomDocument *doc) const
{
  if(attachTable.size()==0)
    return QDomElement();

  QDomElement attachTableDom=doc->createElement("files");

  for(int i=0; i<attachTable.size(); i++)
    attachTableDom.appendChild( attachTable.at(i).exportDataToDom( doc ) ); // К элементу files прикрепляются элементы file

  return attachTableDom;
}

void AttachTableData::exportDataToStreamWriter(QXmlStreamWriter *xmlWriter) const
{
  if(attachTable.size()==0)
    return;

  xmlWriter->writeStartElement("files");

  for(int i=0; i<attachTable.size(); i++)
    attachTable.at(i).exportDataToStreamWriter( xmlWriter ); // К элементу files прикрепляются элементы file

  xmlWriter->writeEndElement(); // Закрылся files
}

void AttachTableData::setRelatedAttachTableModel(AttachTableModel *model)
{
  relatedAttachTableModel=model;

  relatedAttachTableModel->setData(QModelIndex(), QVariant::fromValue(this), ATTACHTABLE_ROLE_TABLE_DATA_ONLY);
}

void AttachTableData::setRelatedAttachTableModelOnly(AttachTableModel *model)
{
  relatedAttachTableModel=model;
}

bool AttachTableData::isEmpty() const
{
  if(attachTable.size()==0)
    return true;
  else
    return false;
}

bool AttachTableData::isLite() const
{
  return liteFlag;
}

void AttachTableData::setRecord(Record *iRecord)
{
  record=iRecord; // Запоминается ссылка на запись, которой принадлежит данная таблица файлов
}

void AttachTableData::clear()
{
  attachTable.clear();
  record=NULL;
  liteFlag=true;
}

int AttachTableData::size() const
{
  return attachTable.size();
}

Attach AttachTableData::getAttach(QString id)
{
  int row=getRowById(id);

  if(row<0)
    criticalError("Attach with ID: "+id+" not found");

  return attachTable.at(row);
}

void AttachTableData::addAttach(Attach attach)
{
  if(relatedAttachTableModel!=NULL)
    relatedAttachTableModel->setData(QModelIndex(), QVariant(), ATTACHTABLE_COMMAND_BEGIN_RESET_MODEL);

  attachTable.append(attach);

  if(relatedAttachTableModel!=NULL)
    relatedAttachTableModel->setData(QModelIndex(), QVariant(), ATTACHTABLE_COMMAND_END_RESET_MODEL);
}

void AttachTableData::modifyAttach(QString id, Attach iAttach)
{
  int row=getRowById(id);

  if(row<0)
    return;

  attachTable[row]=iAttach;
}

void AttachTableData::deleteAttach(QString id)
{
  int row=getRowById(id);

  if(row<0)
    return;

  attachTable[row].removeFile();

  if(relatedAttachTableModel==NULL)
  {
    attachTable.removeAt(row); // Просто удаляется запись в данных
    return;
  }
  else
  {

    relatedAttachTableModel->setData(QModelIndex(), row, ATTACHTABLE_COMMAND_BEGIN_REMOVE_ROW);
    attachTable.removeAt(row); // Удаляется запись в данных
    relatedAttachTableModel->setData(QModelIndex(), QVariant(), ATTACHTABLE_COMMAND_END_REMOVE_ROW);
  }
}

int AttachTableData::getRowById(QString id)
{
  for(int i=0; i<attachTable.size(); i++)
    if(attachTable.at(i).getField("id")==id)
      return i;

  return -1;
}

QString AttachTableData::getIdByRow(int row)
{
  return attachTable.at(row).getField("id");
}

QString AttachTableData::getFileName(int row)
{
  return attachTable.at(row).getField("fileName");
}

QString AttachTableData::getFileNameById(QString id)
{
  int row=getRowById(id);

  if(row>=0)
    return getFileName(row);
  else
    return "";
}

QString AttachTableData::getInnerFileName(int row)
{
  return attachTable.at(row).getInnerFileName();
}

QString AttachTableData::getInnerFileNameById(QString id)
{
  int row=getRowById(id);

  if(row>=0)
    return getInnerFileName(row);
  else
    return "";
}

QStringList AttachTableData::getInnerFileNameList()
{
  QStringList list;

  for(int i=0; i<attachTable.count(); ++i)
    list.append( attachTable.at(i).getInnerFileName() );

  return list;
}

QStringList AttachTableData::getInnerFileNameOnDiskList()
{
  QStringList list;

  for(int i=0; i<attachTable.count(); ++i)
    if( attachTable.at(i).getField("type")!="link" )
      list.append( attachTable.at(i).getInnerFileName() );

  return list;
}

QString AttachTableData::getFullInnerFileName(int row)
{
  return attachTable.at(row).getFullInnerFileName();
}

QString AttachTableData::getFullInnerFileNameById(QString id)
{
  int row=getRowById(id);

  if(row>=0)
    return getFullInnerFileName(row);
  else
    return "";
}

QString AttachTableData::getAbsoluteInnerFileName(int row)
{
  return attachTable.at(row).getAbsoluteInnerFileName();
}

QString AttachTableData::getAbsoluteInnerFileNameById(QString id)
{
  int row=getRowById(id);

  if(row>=0)
    return getAbsoluteInnerFileName(row);
  else
    return "";
}

qint64 AttachTableData::getFileSize(int row)
{
  return attachTable.at(row).getFileSize();
}

{
  for(int i=0; i<attachTable.size(); ++i)
  {
    qDebug() << "File: " << attachTable.at(i).getField("id") << " Type: " << attachTable.at(i).getField("type");
  }
}

void AttachTableData::switchToLite()
{
  if(liteFlag==true)
    criticalError("Can't switch attach table to lite state");

  for(int i=0; i<attachTable.size(); ++i)
  {
    if(attachTable[i].getField("type")=="file")
      attachTable[i].pushFatDataToDisk();

    attachTable[i].switchToLite();
  }

  liteFlag=true;
}

void AttachTableData::switchToFat()
{
  if(liteFlag!=true)
    criticalError("Unavailable switching attach table to fat state");

  for(int i=0; i<attachTable.size(); ++i)
  {
    attachTable[i].switchToFat();

    if(attachTable[i].getField("type")=="file")
      attachTable[i].popFatDataFromDisk();
  }

  liteFlag=false;
}

bool AttachTableData::isRecordCrypt()
{
  if( record->getField("crypt")=="1" )
    return true;
  else
    return false;
}

void AttachTableData::encrypt(unsigned int area)
{
  for(int i=0; i<attachTable.size(); ++i)
    attachTable[i].encrypt(area);
}

void AttachTableData::decrypt(unsigned int area)
{
  for(int i=0; i<attachTable.size(); ++i)
    attachTable[i].decrypt(area);
}

void AttachTableData::saveAttachFilesToDirectory(QString dirName)
{
  for(int i=0; i<attachTable.size(); ++i)
    if(attachTable.at(i).getField("type")=="file") // Сохраняются только файлы, не линки
      if(!attachTable.at(i).isLite())
        attachTable[i].pushFatDataToDirectory(dirName);
}

void AttachTableData::updateAttachTableBackLink()
{
  for(int i=0; i<attachTable.size(); ++i)
    attachTable[i].setParentTable(this);
}


{
  int row=getRowById(id);

  if(row>=0)
    return getFileName(row);
  else
    return "";
}

#include <QDomElement>
#include <QDebug>
#include <QFileInfo>

#include "main.h"
#include "Attach.h"
#include "AttachTableData.h"
#include "AttachTableModel.h"
#include "models/recordTable/Record.h"
#include "libraries/helpers/DebugHelper.h"

AttachTableData::AttachTableData(Record *iRecord)
{
  liteFlag=true;
  attachTable.clear();
  record=iRecord;
  relatedAttachTableModel=NULL;
}

/*
AttachTableData::AttachTableData(const AttachTableData &obj)
{
  attachTable=new QList< Attach >(obj.attachTable); // "Глубокое" копирование таблицы прикрепляемых файлов
  record=obj.record; // А ссылка на запись просто копируется
}
*/

AttachTableData::AttachTableData()
{
  liteFlag=true;
  attachTable.clear();
  record=NULL;
  relatedAttachTableModel=NULL;
}

AttachTableData::~AttachTableData()
{

}

void AttachTableData::setupDataFromDom(QDomElement iDomElement)
{
  QDomElement currentFile=iDomElement.firstChildElement("file");

  while(!currentFile.isNull())
  {
    Attach attach(this);

    attachTable.append(attach);

    attachTable.last().setupDataFromDom(currentFile);

    currentFile=currentFile.nextSiblingElement("file");
  }
}

QDomElement AttachTableData::exportDataToDom(QDomDocument *doc) const
{
  if(attachTable.size()==0)
    return QDomElement();

  QDomElement attachTableDom=doc->createElement("files");

  for(int i=0; i<attachTable.size(); i++)
    attachTableDom.appendChild( attachTable.at(i).exportDataToDom( doc ) ); // К элементу files прикрепляются элементы file

  return attachTableDom;
}

void AttachTableData::exportDataToStreamWriter(QXmlStreamWriter *xmlWriter) const
{
  if(attachTable.size()==0)
    return;

  xmlWriter->writeStartElement("files");

  for(int i=0; i<attachTable.size(); i++)
    attachTable.at(i).exportDataToStreamWriter( xmlWriter ); // К элементу files прикрепляются элементы file

  xmlWriter->writeEndElement(); // Закрылся files
}

void AttachTableData::setRelatedAttachTableModel(AttachTableModel *model)
{
  relatedAttachTableModel=model;

  relatedAttachTableModel->setData(QModelIndex(), QVariant::fromValue(this), ATTACHTABLE_ROLE_TABLE_DATA_ONLY);
}

void AttachTableData::setRelatedAttachTableModelOnly(AttachTableModel *model)
{
  relatedAttachTableModel=model;
}

bool AttachTableData::isEmpty() const
{
  if(attachTable.size()==0)
    return true;
  else
    return false;
}

bool AttachTableData::isLite() const
{
  return liteFlag;
}

void AttachTableData::setRecord(Record *iRecord)
{
  record=iRecord; // Запоминается ссылка на запись, которой принадлежит данная таблица файлов
}

void AttachTableData::clear()
{
  attachTable.clear();
  record=NULL;
  liteFlag=true;
}

int AttachTableData::size() const
{
  return attachTable.size();
}

Attach AttachTableData::getAttach(QString id)
{
  int row=getRowById(id);

  if(row<0)
    criticalError("Attach with ID: "+id+" not found");

  return attachTable.at(row);
}

void AttachTableData::addAttach(Attach attach)
{
  if(relatedAttachTableModel!=NULL)
    relatedAttachTableModel->setData(QModelIndex(), QVariant(), ATTACHTABLE_COMMAND_BEGIN_RESET_MODEL);

  attachTable.append(attach);

  if(relatedAttachTableModel!=NULL)
    relatedAttachTableModel->setData(QModelIndex(), QVariant(), ATTACHTABLE_COMMAND_END_RESET_MODEL);
}

void AttachTableData::modifyAttach(QString id, Attach iAttach)
{
  int row=getRowById(id);

  if(row<0)
    return;

  attachTable[row]=iAttach;
}

void AttachTableData::deleteAttach(QString id)
{
  int row=getRowById(id);

  if(row<0)
    return;

  attachTable[row].removeFile();

  if(relatedAttachTableModel==NULL)
  {
    attachTable.removeAt(row); // Просто удаляется запись в данных
    return;
  }
  else
  {

    relatedAttachTableModel->setData(QModelIndex(), row, ATTACHTABLE_COMMAND_BEGIN_REMOVE_ROW);
    attachTable.removeAt(row); // Удаляется запись в данных
    relatedAttachTableModel->setData(QModelIndex(), QVariant(), ATTACHTABLE_COMMAND_END_REMOVE_ROW);
  }
}

int AttachTableData::getRowById(QString id)
{
  for(int i=0; i<attachTable.size(); i++)
    if(attachTable.at(i).getField("id")==id)
      return i;

  return -1;
}

QString AttachTableData::getIdByRow(int row)
{
  return attachTable.at(row).getField("id");
}

QString AttachTableData::getFileName(int row)
{
  return attachTable.at(row).getField("fileName");
}

QString AttachTableData::getFileNameById(QString id)

QString AttachTableData::getInnerFileName(int row)
{
  return attachTable.at(row).getInnerFileName();
}

QString AttachTableData::getInnerFileNameById(QString id)
{
  int row=getRowById(id);

  if(row>=0)
    return getInnerFileName(row);
  else
    return "";
}

QStringList AttachTableData::getInnerFileNameList()
{
  QStringList list;

  for(int i=0; i<attachTable.count(); ++i)
    list.append( attachTable.at(i).getInnerFileName() );

  return list;
}

QStringList AttachTableData::getInnerFileNameOnDiskList()
{
  QStringList list;

  for(int i=0; i<attachTable.count(); ++i)
    if( attachTable.at(i).getField("type")!="link" )
      list.append( attachTable.at(i).getInnerFileName() );

  return list;
}

QString AttachTableData::getFullInnerFileName(int row)
{
  return attachTable.at(row).getFullInnerFileName();
}

QString AttachTableData::getFullInnerFileNameById(QString id)
{
  int row=getRowById(id);

  if(row>=0)
    return getFullInnerFileName(row);
  else
    return "";
}

QString AttachTableData::getAbsoluteInnerFileName(int row)
{
  return attachTable.at(row).getAbsoluteInnerFileName();
}

QString AttachTableData::getAbsoluteInnerFileNameById(QString id)
{
  int row=getRowById(id);

  if(row>=0)
    return getAbsoluteInnerFileName(row);
  else
    return "";
}

qint64 AttachTableData::getFileSize(int row)
{
  return attachTable.at(row).getFileSize();
}

void AttachTableData::print()
{
  for(int i=0; i<attachTable.size(); ++i)
  {
    qDebug() << "File: " << attachTable.at(i).getField("id") << " Type: " << attachTable.at(i).getField("type");
  }
}

void AttachTableData::switchToLite()
{
  if(liteFlag==true)
    criticalError("Can't switch attach table to lite state");

  for(int i=0; i<attachTable.size(); ++i)
  {
    if(attachTable[i].getField("type")=="file")
      attachTable[i].pushFatDataToDisk();

    attachTable[i].switchToLite();
  }

  liteFlag=true;
}

void AttachTableData::switchToFat()
{
  if(liteFlag!=true)
    criticalError("Unavailable switching attach table to fat state");

  for(int i=0; i<attachTable.size(); ++i)
  {
    attachTable[i].switchToFat();

    if(attachTable[i].getField("type")=="file")
      attachTable[i].popFatDataFromDisk();
  }

  liteFlag=false;
}

bool AttachTableData::isRecordCrypt()
{
  if( record->getField("crypt")=="1" )
    return true;
  else
    return false;
}

void AttachTableData::encrypt(unsigned int area)
{
  for(int i=0; i<attachTable.size(); ++i)
    attachTable[i].encrypt(area);
}

void AttachTableData::decrypt(unsigned int area)
{
  for(int i=0; i<attachTable.size(); ++i)
    attachTable[i].decrypt(area);
}

void AttachTableData::saveAttachFilesToDirectory(QString dirName)
{
  for(int i=0; i<attachTable.size(); ++i)
    if(attachTable.at(i).getField("type")=="file") // Сохраняются только файлы, не линки
      if(!attachTable.at(i).isLite())
        attachTable[i].pushFatDataToDirectory(dirName);
}

void AttachTableData::updateAttachTableBackLink()
{
  for(int i=0; i<attachTable.size(); ++i)
    attachTable[i].setParentTable(this);
}


#include <QDomElement>
#include <QDebug>
#include <QFileInfo>

#include "main.h"
#include "Attach.h"
#include "AttachTableData.h"
#include "AttachTableModel.h"
#include "models/recordTable/Record.h"
#include "libraries/helpers/DebugHelper.h"

AttachTableData::AttachTableData(Record *iRecord)
{
  liteFlag=true;
  attachTable.clear();
  record=iRecord;
  relatedAttachTableModel=NULL;
}

/*
AttachTableData::AttachTableData(const AttachTableData &obj)
{
  attachTable=new QList< Attach >(obj.attachTable); // "Глубокое" копирование таблицы прикрепляемых файлов
  record=obj.record; // А ссылка на запись просто копируется
}
*/

AttachTableData::AttachTableData()
{
  liteFlag=true;
  attachTable.clear();
  record=NULL;
  relatedAttachTableModel=NULL;
}

AttachTableData::~AttachTableData()
{

}

void AttachTableData::setupDataFromDom(QDomElement iDomElement)
{
  QDomElement currentFile=iDomElement.firstChildElement("file");

  while(!currentFile.isNull())
  {
    Attach attach(this);

    attachTable.append(attach);

    attachTable.last().setupDataFromDom(currentFile);

    currentFile=currentFile.nextSiblingElement("file");
  }
}

QDomElement AttachTableData::exportDataToDom(QDomDocument *doc) const
{
  if(attachTable.size()==0)
    return QDomElement();

  QDomElement attachTableDom=doc->createElement("files");

  for(int i=0; i<attachTable.size(); i++)
    attachTableDom.appendChild( attachTable.at(i).exportDataToDom( doc ) ); // К элементу files прикрепляются элементы file

  return attachTableDom;
}

void AttachTableData::exportDataToStreamWriter(QXmlStreamWriter *xmlWriter) const
{
  if(attachTable.size()==0)
    return;

  xmlWriter->writeStartElement("files");

  for(int i=0; i<attachTable.size(); i++)
    attachTable.at(i).exportDataToStreamWriter( xmlWriter ); // К элементу files прикрепляются элементы file

  xmlWriter->writeEndElement(); // Закрылся files
}

void AttachTableData::setRelatedAttachTableModel(AttachTableModel *model)
{
  relatedAttachTableModel=model;

  relatedAttachTableModel->setData(QModelIndex(), QVariant::fromValue(this), ATTACHTABLE_ROLE_TABLE_DATA_ONLY);
}

void AttachTableData::setRelatedAttachTableModelOnly(AttachTableModel *model)
{
  relatedAttachTableModel=model;
}

bool AttachTableData::isEmpty() const
{
  if(attachTable.size()==0)
    return true;
  else
    return false;
}

bool AttachTableData::isLite() const
{
  return liteFlag;
}

void AttachTableData::setRecord(Record *iRecord)
{
  record=iRecord; // Запоминается ссылка на запись, которой принадлежит данная таблица файлов
}

void AttachTableData::clear()
{
  attachTable.clear();
  record=NULL;
  liteFlag=true;
}

int AttachTableData::size() const
{
  return attachTable.size();
}

Attach AttachTableData::getAttach(QString id)
{
  int row=getRowById(id);

  if(row<0)
    criticalError("Attach with ID: "+id+" not found");

  return attachTable.at(row);
}

void AttachTableData::addAttach(Attach attach)
{
  if(relatedAttachTableModel!=NULL)
    relatedAttachTableModel->setData(QModelIndex(), QVariant(), ATTACHTABLE_COMMAND_BEGIN_RESET_MODEL);

  attachTable.append(attach);

  if(relatedAttachTableModel!=NULL)
    relatedAttachTableModel->setData(QModelIndex(), QVariant(), ATTACHTABLE_COMMAND_END_RESET_MODEL);
}

void AttachTableData::modifyAttach(QString id, Attach iAttach)
{
  int row=getRowById(id);

  if(row<0)
    return;

  attachTable[row]=iAttach;
}

void AttachTableData::deleteAttach(QString id)
{
  int row=getRowById(id);

  if(row<0)
    return;

  attachTable[row].removeFile();

  if(relatedAttachTableModel==NULL)
  {
    attachTable.removeAt(row); // Просто удаляется запись в данных
    return;
  }
  else
  {

    relatedAttachTableModel->setData(QModelIndex(), row, ATTACHTABLE_COMMAND_BEGIN_REMOVE_ROW);
    attachTable.removeAt(row); // Удаляется запись в данных
    relatedAttachTableModel->setData(QModelIndex(), QVariant(), ATTACHTABLE_COMMAND_END_REMOVE_ROW);
  }
}

int AttachTableData::getRowById(QString id)
{
  for(int i=0; i<attachTable.size(); i++)
    if(attachTable.at(i).getField("id")==id)
      return i;

  return -1;
}

QString AttachTableData::getIdByRow(int row)
{
  return attachTable.at(row).getField("id");
}

QString AttachTableData::getFileName(int row)
{
  return attachTable.at(row).getField("fileName");
}

QString AttachTableData::getFileNameById(QString id)
{
  int row=getRowById(id);

  if(row>=0)
    return getFileName(row);
  else
    return "";
}

QString AttachTableData::getInnerFileName(int row)
{
  return attachTable.at(row).getInnerFileName();
}

QString AttachTableData::getInnerFileNameById(QString id)
{
  int row=getRowById(id);

  if(row>=0)
    return getInnerFileName(row);
  else
    return "";
}

QStringList AttachTableData::getInnerFileNameList()
{
  QStringList list;

  for(int i=0; i<attachTable.count(); ++i)
    list.append( attachTable.at(i).getInnerFileName() );

  return list;
}

QStringList AttachTableData::getInnerFileNameOnDiskList()
{
  QStringList list;

  for(int i=0; i<attachTable.count(); ++i)
    if( attachTable.at(i).getField("type")!="link" )
      list.append( attachTable.at(i).getInnerFileName() );

  return list;
}

QString AttachTableData::getFullInnerFileName(int row)
{
  return attachTable.at(row).getFullInnerFileName();
}

QString AttachTableData::getFullInnerFileNameById(QString id)
{
  int row=getRowById(id);

  if(row>=0)
    return getFullInnerFileName(row);
  else
    return "";
}

QString AttachTableData::getAbsoluteInnerFileName(int row)
{
  return attachTable.at(row).getAbsoluteInnerFileName();
}

QString AttachTableData::getAbsoluteInnerFileNameById(QString id)
{
  int row=getRowById(id

  if(row>=0)
    return getAbsoluteInnerFileName(row);
  else
    return "";
}

qint64 AttachTableData::getFileSize(int row)
{
  return attachTable.at(row).getFileSize();
}

void AttachTableData::print()
{
  for(int i=0; i<attachTable.size(); ++i)
  {
    qDebug() << "File: " << attachTable.at(i).getField("id") << " Type: " << attachTable.at(i).getField("type");
  }
}

void AttachTableData::switchToLite()
{
  if(liteFlag==true)
    criticalError("Can't switch attach table to lite state");

  for(int i=0; i<attachTable.size(); ++i)
  {
    if(attachTable[i].getField("type")=="file")
      attachTable[i].pushFatDataToDisk();

    attachTable[i].switchToLite();
  }

  liteFlag=true;
}

void AttachTableData::switchToFat()
{
  if(liteFlag!=true)
    criticalError("Unavailable switching attach table to fat state");

  for(int i=0; i<attachTable.size(); ++i)
  {
    attachTable[i].switchToFat();

    if(attachTable[i].getField("type")=="file")
      attachTable[i].popFatDataFromDisk();
  }

  liteFlag=false;
}

bool AttachTableData::isRecordCrypt()
{
  if( record->getField("crypt")=="1" )
    return true;
  else
    return false;
}

void AttachTableData::encrypt(unsigned int area)
{
  for(int i=0; i<attachTable.size(); ++i)
    attachTable[i].encrypt(area);
}

void AttachTableData::decrypt(unsigned int area)
{
  for(int i=0; i<attachTable.size(); ++i)
    attachTable[i].decrypt(area);
}

void AttachTableData::saveAttachFilesToDirectory(QString dirName)
{
  for(int i=0; i<attachTable.size(); ++i)
    if(attachTable.at(i).getField("type")=="file") // Сохраняются только файлы, не линки
      if(!attachTable.at(i).isLite())
        attachTable[i].pushFatDataToDirectory(dirName);
}

void AttachTableData::updateAttachTableBackLink()
{
  for(int i=0; i<attachTable.size(); ++i)
    attachTable[i].setParentTable(this);
}


AttachTableData::AttachTableData()
{
  liteFlag=true;
  attachTable.clear();
  record=NULL;
  relatedAttachTableModel=NULL;
}

#include <QDomElement>
#include <QDebug>
#include <QFileInfo>

#include "main.h"
#include "Attach.h"
#include "AttachTableData.h"
#include "AttachTableModel.h"
#include "models/recordTable/Record.h"
#include "libraries/helpers/DebugHelper.h"

AttachTableData::AttachTableData(Record *iRecord)
{
  liteFlag=true;
  attachTable.clear();
  record=iRecord;
  relatedAttachTableModel=NULL;
}

/*
AttachTableData::AttachTableData(const AttachTableData &obj)
{
  attachTable=new QList< Attach >(obj.attachTable); // "Глубокое" копирование таблицы прикрепляемых файлов
  record=obj.record; // А ссылка на запись просто копируется
}
*/

AttachTableData::~AttachTableData()
{

}

void AttachTableData::setupDataFromDom(QDomElement iDomElement)
{
  QDomElement currentFile=iDomElement.firstChildElement("file");

  while(!currentFile.isNull())
  {
    Attach attach(this);

    attachTable.append(attach);

    attachTable.last().setupDataFromDom(currentFile);

    currentFile=currentFile.nextSiblingElement("file");
  }
}

QDomElement AttachTableData::exportDataToDom(QDomDocument *doc) const
{
  if(attachTable.size()==0)
    return QDomElement();

  QDomElement attachTableDom=doc->createElement("files");

  for(int i=0; i<attachTable.size(); i++)
    attachTableDom.appendChild( attachTable.at(i).exportDataToDom( doc ) ); // К элементу files прикрепляются элементы file

  return attachTableDom;
}

void AttachTableData::exportDataToStreamWriter(QXmlStreamWriter *xmlWriter) const
{
  if(attachTable.size()==0)
    return;

  xmlWriter->writeStartElement("files");

  for(int i=0; i<attachTable.size(); i++)
    attachTable.at(i).exportDataToStreamWriter( xmlWriter ); // К элементу files прикрепляются элементы file

  xmlWriter->writeEndElement(); // Закрылся files
}

void AttachTableData::setRelatedAttachTableModel(AttachTableModel *model)
{
  relatedAttachTableModel=model;

  relatedAttachTableModel->setData(QModelIndex(), QVariant::fromValue(this), ATTACHTABLE_ROLE_TABLE_DATA_ONLY);
}

void AttachTableData::setRelatedAttachTableModelOnly(AttachTableModel *model)
{
  relatedAttachTableModel=model;
}

bool AttachTableData::isEmpty() const
{
  if(attachTable.size()==0)
    return true;
  else
    return false;
}

bool AttachTableData::isLite() const
{
  return liteFlag;
}

void AttachTableData::setRecord(Record *iRecord)
{
  record=iRecord; // Запоминается ссылка на запись, которой принадлежит данная таблица файлов
}

void AttachTableData::clear()
{
  attachTable.clear();
  record=NULL;
  liteFlag=true;
}

int AttachTableData::size() const
{
  return attachTable.size();
}

Attach AttachTableData::getAttach(QString id)
{
  int row=getRowById(id);

  if(row<0)
    criticalError("Attach with ID: "+id+" not found");

  return attachTable.at(row);
}

void AttachTableData::addAttach(Attach attach)
{
  if(relatedAttachTableModel!=NULL)
    relatedAttachTableModel->setData(QModelIndex(), QVariant(), ATTACHTABLE_COMMAND_BEGIN_RESET_MODEL);

  attachTable.append(attach);

  if(relatedAttachTableModel!=NULL)
    relatedAttachTableModel->setData(QModelIndex(), QVariant(), ATTACHTABLE_COMMAND_END_RESET_MODEL);
}

void AttachTableData::modifyAttach(QString id, Attach iAttach)
{
  int row=getRowById(id);

  if(row<0)
    return;

  attachTable[row]=iAttach;
}

void AttachTableData::deleteAttach(QString id)
{
  int row=getRowById(id);

  if(row<0)
    return;

  attachTable[row].removeFile();

  if(relatedAttachTableModel==NULL)
  {
    attachTable.removeAt(row); // Просто удаляется запись в данных
    return;
  }
  else
  {

    relatedAttachTableModel->setData(QModelIndex(), row, ATTACHTABLE_COMMAND_BEGIN_REMOVE_ROW);
    attachTable.removeAt(row); // Удаляется запись в данных
    relatedAttachTableModel->setData(QModelIndex(), QVariant(), ATTACHTABLE_COMMAND_END_REMOVE_ROW);
  }
}

int AttachTableData::getRowById(QString id)
{
  for(int i=0; i<attachTable.size(); i++)
    if(attachTable.at(i).getField("id")==id)
      return i;

  return -1;
}

QString AttachTableData::getIdByRow(int row)
{
  return attachTable.at(row).getField("id");
}

QString AttachTableData::getFileName(int row)
{
  return attachTable.at(row).getField("fileName");
}

QString AttachTableData::getFileNameById(QString id)
{
  int row=getRowById(id);

  if(row>=0)
    return getFileName(row);
  else
    return "";
}

QString AttachTableData::getInnerFileName(int row)
{
  return attachTable.at(row).getInnerFileName();
}

QString AttachTableData::getInnerFileNameById(QString id)
{
  int row=getRowById(id);

  if(row>=0)
    return getInnerFileName(row);
  else
    return "";
}

QStringList AttachTableData::getInnerFileNameList()
{
  QStringList list;

  for(int i=0; i<attachTable.count(); ++i)
    list.append( attachTable.at(i).getInnerFileName() );

  return list;
}

QStringList AttachTableData::getInnerFileNameOnDiskList()
{
  QStringList list;

  for(int i=0; i<attachTable.count(); ++i)
    if( attachTable.at(i).getField("type")!="link" )
      list.append( attachTable.at(i).getInnerFileName() );

  return list;
}

QString AttachTableData::getFullInnerFileName(int row)
{
  return attachTable.at(row).getFullInnerFileName();
}

QString AttachTableData::getFullInnerFileNameById(QString id)
{
  int row=getRowById(id);

  if(row>=0)
    return getFullInnerFileName(row);
  else
    return "";
}

QString AttachTableData::getAbsoluteInnerFileName(int row)
{
  return attachTable.at(row).getAbsoluteInnerFileName();
}

QString AttachTableData::getAbsoluteInnerFileNameById(QString id)
{
  int row=getRowById(id);

  if(row>=0)
    return getAbsoluteInnerFileName(row);
  else
    return "";
}

qint64 AttachTableData::getFileSize(int row)
{
  return attachTable.at(row).getFileSize();
}

void AttachTableData::print()
{
  for(int i=0; i<attachTable.size(); ++i)
  {
    qDebug() << "File: " << attachTable.at(i).getField("id") << " Type: " << attachTable.at(i).getField("type");
  }
}

void AttachTableData::switchToLite()
{
  if(liteFlag==true)
    criticalError("Can't switch attach table to lite state");

  for(int i=0; i<attachTable.size(); ++i)
  {
    if(attachTable[i].getField("type")=="file")
      attachTable[i].pushFatDataToDisk();

    attachTable[i].switchToLite();
  }

  liteFlag=true;
}

void AttachTableData::switchToFat()
{
  if(liteFlag!=true)
    criticalError("Unavailable switching attach table to fat state");

  for(int i=0; i<attachTable.size(); ++i)
  {
    attachTable[i].switchToFat();

    if(attachTable[i].getField("type")=="file")
      attachTable[i].popFatDataFromDisk();
  }

  liteFlag=false;
}

bool AttachTableData::isRecordCrypt()
{
  if( record->getField("crypt")=="1" )
    return true;
  else
    return false;
}

void AttachTableData::encrypt(unsigned int area)
{
  for(int i=0; i<attachTable.size(); ++i)
    attachTable[i].encrypt(area);
}

void AttachTableData::decrypt(unsigned int area)
{
  for(int i=0; i<attachTable.size(); ++i)
    attachTable[i].decrypt(area);
}

void AttachTableData::saveAttachFilesToDirectory(QString dirName)
{
  for(int i=0; i<attachTable.size(); ++i)
    if(attachTable.at(i).getField("type")=="file") // Сохраняются только файлы, не линки
      if(!attachTable.at(i).isLite())
        attachTable[i].pushFatDataToDirectory(dirName);
}

void AttachTableData::updateAttachTableBackLink()
{
  for(int i=0; i<attachTable.size(); ++i)
    attachTable[i].setParentTable(this);
}


while(!currentFile.isNull())
  {
    Attach attach(this);

    attachTable.append(attach);

    attachTable.last().setupDataFromDom(currentFile);

    currentFile=currentFile.nextSiblingElement("file");
  }

#include <QDomElement>
#include <QDebug>
#include <QFileInfo>

#include "main.h"
#include "Attach.h"
#include "AttachTableData.h"
#include "AttachTableModel.h"
#include "models/recordTable/Record.h"
#include "libraries/helpers/DebugHelper.h"

AttachTableData::AttachTableData(Record *iRecord)
{
  liteFlag=true;
  attachTable.clear();
  record=iRecord;
  relatedAttachTableModel=NULL;
}

/*
AttachTableData::AttachTableData(const AttachTableData &obj)
{
  attachTable=new QList< Attach >(obj.attachTable); // "Глубокое" копирование таблицы прикрепляемых файлов
  record=obj.record; // А ссылка на запись просто копируется
}
*/

AttachTableData::AttachTableData()
{
  liteFlag=true;
  attachTable.clear();
  record=NULL;
  relatedAttachTableModel=NULL;
}

AttachTableData::~AttachTableData()
{

}

void AttachTableData::setupDataFromDom(QDomElement iDomElement)
{
  QDomElement currentFile=iDomElement.firstChildElement("file");

}

QDomElement AttachTableData::exportDataToDom(QDomDocument *doc) const
{
  if(attachTable.size()==0)
    return QDomElement();

  QDomElement attachTableDom=doc->createElement("files");

  for(int i=0; i<attachTable.size(); i++)
    attachTableDom.appendChild( attachTable.at(i).exportDataToDom( doc ) ); // К элементу files прикрепляются элементы file

  return attachTableDom;
}

void AttachTableData::exportDataToStreamWriter(QXmlStreamWriter *xmlWriter) const
{
  if(attachTable.size()==0)
    return;

  xmlWriter->writeStartElement("files");

  for(int i=0; i<attachTable.size(); i++)
    attachTable.at(i).exportDataToStreamWriter( xmlWriter ); // К элементу files прикрепляются элементы file

  xmlWriter->writeEndElement(); // Закрылся files
}

void AttachTableData::setRelatedAttachTableModel(AttachTableModel *model)
{
  relatedAttachTableModel=model;

  relatedAttachTableModel->setData(QModelIndex(), QVariant::fromValue(this), ATTACHTABLE_ROLE_TABLE_DATA_ONLY);
}

void AttachTableData::setRelatedAttachTableModelOnly(AttachTableModel *model)
{
  relatedAttachTableModel=model;
}

bool AttachTableData::isEmpty() const
{
  if(attachTable.size()==0)
    return true;
  else
    return false;
}

bool AttachTableData::isLite() const
{
  return liteFlag;
}

void AttachTableData::setRecord(Record *iRecord)
{
  record=iRecord; // Запоминается ссылка на запись, которой принадлежит данная таблица файлов
}

void AttachTableData::clear()
{
  attachTable.clear();
  record=NULL;
  liteFlag=true;
}

int AttachTableData::size() const
{
  return attachTable.size();
}

Attach AttachTableData::getAttach(QString id)
{
  int row=getRowById(id);

  if(row<0)
    criticalError("Attach with ID: "+id+" not found");

  return attachTable.at(row);
}

void AttachTableData::addAttach(Attach attach)
{
  if(relatedAttachTableModel!=NULL)
    relatedAttachTableModel->setData(QModelIndex(), QVariant(), ATTACHTABLE_COMMAND_BEGIN_RESET_MODEL);

  attachTable.append(attach);

  if(relatedAttachTableModel!=NULL)
    relatedAttachTableModel->setData(QModelIndex(), QVariant(), ATTACHTABLE_COMMAND_END_RESET_MODEL);
}

void AttachTableData::modifyAttach(QString id, Attach iAttach)
{
  int row=getRowById(id);

  if(row<0)
    return;

  attachTable[row]=iAttach;
}

void AttachTableData::deleteAttach(QString id)
{
  int row=getRowById(id);

  if(row<0)
    return;

  attachTable[row].removeFile();

  if(relatedAttachTableModel==NULL)
  {
    attachTable.removeAt(row); // Просто удаляется запись в данных
    return;
  }
  else
  {

    relatedAttachTableModel->setData(QModelIndex(), row, ATTACHTABLE_COMMAND_BEGIN_REMOVE_ROW);
    attachTable.removeAt(row); // Удаляется запись в данных
    relatedAttachTableModel->setData(QModelIndex(), QVariant(), ATTACHTABLE_COMMAND_END_REMOVE_ROW);
  }
}

int AttachTableData::getRowById(QString id)
{
  for(int i=0; i<attachTable.size(); i++)
    if(attachTable.at(i).getField("id")==id)
      return i;

  return -1;
}

QString AttachTableData::getIdByRow(int row)
{
  return attachTable.at(row).getField("id");
}

QString AttachTableData::getFileName(int row)
{
  return attachTable.at(row).getField("fileName");
}

QString AttachTableData::getFileNameById(QString id)
{
  int row=getRowById(id);

  if(row>=0)
    return getFileName(row);
  else
    return "";
}

QString AttachTableData::getInnerFileName(int row)
{
  return attachTable.at(row).getInnerFileName();
}

QString AttachTableData::getInnerFileNameById(QString id)
{
  int row=getRowById(id);

  if(row>=0)
    return getInnerFileName(row);
  else
    return "";
}

QStringList AttachTableData::getInnerFileNameList()
{
  QStringList list;

  for(int i=0; i<attachTable.count(); ++i)
    list.append( attachTable.at(i).getInnerFileName() );

  return list;
}

QStringList AttachTableData::getInnerFileNameOnDiskList()
{
  QStringList list;

  for(int i=0; i<attachTable.count(); ++i)
    if( attachTable.at(i).getField("type")!="link" )
      list.append( attachTable.at(i).getInnerFileName() );

  return list;
}

QString AttachTableData::getFullInnerFileName(int row)
{
  return attachTable.at(row).getFullInnerFileName();
}

QString AttachTableData::getFullInnerFileNameById(QString id)
{
  int row=getRowById(id);

  if(row>=0)
    return getFullInnerFileName(row);
  else
    return "";
}

QString AttachTableData::getAbsoluteInnerFileName(int row)
{
  return attachTable.at(row).getAbsoluteInnerFileName();
}

QString AttachTableData::getAbsoluteInnerFileNameById(QString id)
{
  int row=getRowById(id);

  if(row>=0)
    return getAbsoluteInnerFileName(row);
  else
    return "";
}

qint64 AttachTableData::getFileSize(int row)
{
  return attachTable.at(row).getFileSize();
}

void AttachTableData::print()
{
  for(int i=0; i<attachTable.size(); ++i)
  {
    qDebug() << "File: " << attachTable.at(i).getField("id") << " Type: " << attachTable.at(i).getField("type");
  }
}

void AttachTableData::switchToLite()
{
  if(liteFlag==true)
    criticalError("Can't switch attach table to lite state");

  for(int i=0; i<attachTable.size(); ++i)
  {
    if(attachTable[i].getField("type")=="file")
      attachTable[i].pushFatDataToDisk();

    attachTable[i].switchToLite();
  }

  liteFlag=true;
}

void AttachTableData::switchToFat()
{
  if(liteFlag!=true)
    criticalError("Unavailable switching attach table to fat state");

  for(int i=0; i<attachTable.size(); ++i)
  {
    attachTable[i].switchToFat();

    if(attachTable[i].getField("type")=="file")
      attachTable[i].popFatDataFromDisk();
  }

  liteFlag=false;
}

bool AttachTableData::isRecordCrypt()
{
  if( record->getField("crypt")=="1" )
    return true;
  else
    return false;
}

void AttachTableData::encrypt(unsigned int area)
{
  for(int i=0; i<attachTable.size(); ++i)
    attachTable[i].encrypt(area);
}

void AttachTableData::decrypt(unsigned int area)
{
  for(int i=0; i<attachTable.size(); ++i)
    attachTable[i].decrypt(area);
}

void AttachTableData::saveAttachFilesToDirectory(QString dirName)
{
  for(int i=0; i<attachTable.size(); ++i)
    if(attachTable.at(i).getField("type")=="file") // Сохраняются только файлы, не линки
      if(!attachTable.at(i).isLite())
        attachTable[i].pushFatDataToDirectory(dirName);
}

void AttachTableData::updateAttachTableBackLink()
{
  for(int i=0; i<attachTable.size(); ++i)
    attachTable[i].setParentTable(this);
}


DNSServiceErrorType getAddrInfo(ConnectionRef                    cRef,
                                    DNSServiceRef                    *sdRef,
                                    uint32_t                         interfaceIndex,
                                    DNSServiceProtocol               protocol,
                                    const char                       *hostname,
                                    ServiceGatherer                  *gatherer)

/****************************************************************************
**
**
** This file is part of Qt Creator.
**
**
****************************************************************************/

#include "syssocket.h" // this should be the first header included

#include "servicebrowser_p.h"

#include <QCoreApplication>
#include <QDebug>
#include <QDir>
#include <QFileInfo>
#include <QString>
#include <QStringList>
#include <QProcess>

#ifdef Q_OS_LINUX
#define EMBEDDED_LIB
#endif

#ifdef Q_OS_WIN32
#define EMBEDDED_LIB
#endif

#ifdef EMBEDDED_LIB
#define PID_FILE "/tmp/mdnsd.pid"
#define MDNS_UDS_SERVERPATH "/tmp/mdnsd"

#include "embed/dnssd_ipc.c"
#include "embed/dnssd_clientlib.c"
#include "embed/dnssd_clientstub.c"
#ifdef Q_OS_WIN
#include "embed/DebugServices.c"
#endif

namespace ZeroConf {
namespace Internal {
// represents a zero conf library exposing the dns-sd interface
class EmbeddedZConfLib : public ZConfLib
{
public:
    QString daemonPath;

    EmbeddedZConfLib(const QString &daemonPath, ZConfLib::Ptr fallBack) : ZConfLib(fallBack),
        daemonPath(daemonPath)
    {
        if (daemonPath.isEmpty())
            m_maxErrors = 0;
        if (!daemonPath.isEmpty() && daemonPath.at(0) != '/' && daemonPath.at(0) != '.')
            this->daemonPath = QCoreApplication::applicationDirPath() + QChar('/') + daemonPath;
    }

    ~EmbeddedZConfLib()
    { }

    QString name()
    {
        return QString::fromLatin1("Embedded Dns_sd Library");
    }

    bool tryStartDaemon(ErrorMessage::ErrorLogger *logger)
    {
        if (!daemonPath.isEmpty()) {
            QFileInfo dPath(daemonPath);
            QProcess killall;
            bool killAllFailed = false;
#ifdef Q_OS_WIN
            QString cmd = QLatin1String("taskill /im ") + dPath.fileName()
                    + QLatin1String(" /f /t");
#else
            QString cmd = QLatin1String("killall ") + dPath.fileName()
                    + QLatin1String(" 2> /dev/null");
#endif
            killall.start(cmd);
            if (!killall.waitForStarted()) {
                killAllFailed = true;
            } else {
                killall.closeWriteChannel();
                killall.waitForFinished();
            }
            if (killAllFailed) {
                if (logger)
                    logger->appendError(ErrorMessage::WarningLevel,
                                        ZConfLib::tr("%1 failed to kill other daemons with '%2'.")
                                        .arg(name()).arg(cmd));
                if (DEBUG_ZEROCONF)
                    qDebug() << name() << " had an error trying to kill other daemons with " << cmd;
            }
            QString daemonCmd = daemonPath;
            QStringList daemonArgs;
#ifdef Q_OS_LINUX
            if (QFile::exists("/tmp/mdnsd") && logger)
                logger->appendError(ErrorMessage::WarningLevel,
                                    ZConfLib::tr("%1 detected a file at /tmp/mdnsd, daemon startup will probably fail.")
                                    .arg(name()));
            QString logFile = QString::fromLatin1("/tmp/mdnssd.log");
            static int didFail = 0;
            QFile oldLog(logFile);
            if (didFail > 1 && oldLog.exists()) {
                oldLog.open(QIODevice::ReadOnly);
                if (logger) {
                    QByteArray logBA = oldLog.readAll();
                    logger->appendError(ErrorMessage::NoteLevel,
                                        ZConfLib::tr("%1: log of previous daemon run is: '%2'.\n")
                                        .arg(name())
                                        .arg(QString::fromLatin1(logBA.constData(), logBA.size())));
                    qDebug()<<logBA.size()<<oldLog.error()<<oldLog.errorString();
                }
                oldLog.close();
            }
            if (++didFail > 1)
                daemonArgs << QString::fromLatin1("-debug");
#endif
            if (QProcess::startDetached(daemonCmd, daemonArgs)) {
                QThread::yieldCurrentThread();
                // sleep a bit?
                if (DEBUG_ZEROCONF)
                    qDebug() << name() << " started " << daemonCmd << daemonArgs;
                return true;
            } else {
                this->setError(true, ZConfLib::tr("%1 failed starting embedded daemon at %2.")
                               .arg(name()).arg(daemonPath));
            }
        }
        return false;
    }

    void refDeallocate(DNSServiceRef sdRef)
    {
        embeddedLib::DNSServiceRefDeallocate(sdRef);
    }

    void browserDeallocate(BrowserRef *bRef)
    {
        if (bRef){
            embeddedLib::DNSServiceRefDeallocate(*reinterpret_cast<DNSServiceRef*>(bRef));
            *bRef = 0;
        }
    }

    void stopConnection(ConnectionRef cRef)
    {
        int sock = refSockFD(cRef);
        if (sock>0)
            shutdown(sock, SHUT_RDWR);
    }

    void destroyConnection(ConnectionRef *sdRef)
    {
        if (sdRef) {
            embeddedLib::DNSServiceRefDeallocate(*reinterpret_cast<DNSServiceRef*>(sdRef));
            *sdRef = 0;
        }
    }

    DNSServiceErrorType resolve(ConnectionRef                       cRef,
                                DNSServiceRef                       *sdRef,
                                uint32_t                            interfaceIndex,
                                ZK_IP_Protocol                      /* protocol */,
                                const char                          *name,
                                const char                          *regtype,
                                const char                          *domain,
                                ServiceGatherer                     *gatherer)
    {
        *sdRef = reinterpret_cast<DNSServiceRef>(cRef);
        return embeddedLib::DNSServiceResolve(sdRef, kDNSServiceFlagsShareConnection
                                              | kDNSServiceFlagsTimeout,
                                              interfaceIndex, name, regtype, domain,
                                              &cServiceResolveReply, gatherer);
    }

    DNSServiceErrorType queryRecord(ConnectionRef                       cRef,
                                    DNSServiceRef                       *sdRef,
                                    uint32_t                            interfaceIndex,
                                    const char                          *fullname,
                                    ServiceGatherer                     *gatherer)
    {
        *sdRef = reinterpret_cast<DNSServiceRef>(cRef);
        return embeddedLib::DNSServiceQueryRecord(sdRef, kDNSServiceFlagsShareConnection
                                                  | kDNSServiceFlagsTimeout,
                                                  interfaceIndex, fullname,
                                                  kDNSServiceType_TXT, kDNSServiceClass_IN,
                                                  &cTxtRecordReply , gatherer);
    }

{
        *sdRef = reinterpret_cast<DNSServiceRef>(cRef);
        return embeddedLib::DNSServiceGetAddrInfo(sdRef, kDNSServiceFlagsShareConnection
                                                  | kDNSServiceFlagsTimeout,
                                                  interfaceIndex, protocol,
                                                  hostname, &cAddrReply, gatherer);
    }

    DNSServiceErrorType reconfirmRecord(ConnectionRef /*cRef*/, uint32_t /*interfaceIndex*/,
                                        const char * /*name*/, const char * /*type*/,
                                        const char * /*domain*/, const char * /*fullname*/)
    {
        // reload and force update with in the callback with
        return kDNSServiceErr_Unsupported;
    }

    DNSServiceErrorType browse(ConnectionRef                       cRef,
                               BrowserRef                          *bRef,
                               uint32_t                            interfaceIndex,
                               const char                          *regtype,
                               const char                          *domain,    /* may be NULL */
                               ServiceBrowserPrivate               *browser)
    {
        DNSServiceRef *sdRef = reinterpret_cast<DNSServiceRef *>(bRef);
        *sdRef = reinterpret_cast<DNSServiceRef>(cRef);
        return embeddedLib::DNSServiceBrowse(sdRef, kDNSServiceFlagsShareConnection
                                             /* | kDNSServiceFlagsSuppressUnusable */,
                                             interfaceIndex, regtype, domain, &cBrowseReply,
                                             browser);
    }

    DNSServiceErrorType getProperty(const char *property, void *result, uint32_t *size)
    {
        return embeddedLib::DNSServiceGetProperty(property, result, size);
    }

    RunLoopStatus processOneEventBlock(ConnectionRef cRef)
    {
        if (embeddedLib::DNSServiceProcessResult(reinterpret_cast<DNSServiceRef>(cRef)) != kDNSServiceErr_NoError)
            return ProcessedError;
        return ProcessedOk;
    }

    DNSServiceErrorType createConnection(MainConnection *, ConnectionRef *sdRef)
    {
        return embeddedLib::DNSServiceCreateConnection(reinterpret_cast<DNSServiceRef*>(sdRef));
    }

    int refSockFD(ConnectionRef sdRef)
    {
        return embeddedLib::DNSServiceRefSockFD(reinterpret_cast<DNSServiceRef>(sdRef));
    }
};

ZConfLib::Ptr ZConfLib::createEmbeddedLib(const QString &daemonPath, const ZConfLib::Ptr &fallback)
{
    return ZConfLib::Ptr(new EmbeddedZConfLib(daemonPath, fallback));
}
} // namespace Internal
} // namespace ZeroConf

#else // no embedded lib

namespace ZeroConf {
namespace Internal {

ZConfLib::Ptr ZConfLib::createEmbeddedLib(const QString &, const ZConfLib::Ptr &fallback)
{
    return fallback;
}

} // namespace Internal
} // namespace ZeroConf
#endif


{
        *sdRef = reinterpret_cast<DNSServiceRef>(cRef);
        return embeddedLib::DNSServiceQueryRecord(sdRef, kDNSServiceFlagsShareConnection
                                                  | kDNSServiceFlagsTimeout,
                                                  interfaceIndex, fullname,
                                                  kDNSServiceType_TXT, kDNSServiceClass_IN,
                                                  &cTxtRecordReply , gatherer);
    }

/****************************************************************************
**
**
** This file is part of Qt Creator.
**
**
****************************************************************************/

#include "syssocket.h" // this should be the first header included

#include "servicebrowser_p.h"

#include <QCoreApplication>
#include <QDebug>
#include <QDir>
#include <QFileInfo>
#include <QString>
#include <QStringList>
#include <QProcess>

#ifdef Q_OS_LINUX
#define EMBEDDED_LIB
#endif

#ifdef Q_OS_WIN32
#define EMBEDDED_LIB
#endif

#ifdef EMBEDDED_LIB
#define PID_FILE "/tmp/mdnsd.pid"
#define MDNS_UDS_SERVERPATH "/tmp/mdnsd"

#include "embed/dnssd_ipc.c"
#include "embed/dnssd_clientlib.c"
#include "embed/dnssd_clientstub.c"
#ifdef Q_OS_WIN
#include "embed/DebugServices.c"
#endif

namespace ZeroConf {
namespace Internal {
// represents a zero conf library exposing the dns-sd interface
class EmbeddedZConfLib : public ZConfLib
{
public:
    QString daemonPath;

    EmbeddedZConfLib(const QString &daemonPath, ZConfLib::Ptr fallBack) : ZConfLib(fallBack),
        daemonPath(daemonPath)
    {
        if (daemonPath.isEmpty())
            m_maxErrors = 0;
        if (!daemonPath.isEmpty() && daemonPath.at(0) != '/' && daemonPath.at(0) != '.')
            this->daemonPath = QCoreApplication::applicationDirPath() + QChar('/') + daemonPath;
    }

    ~EmbeddedZConfLib()
    { }

    QString name()
    {
        return QString::fromLatin1("Embedded Dns_sd Library");
    }

    bool tryStartDaemon(ErrorMessage::ErrorLogger *logger)
    {
        if (!daemonPath.isEmpty()) {
            QFileInfo dPath(daemonPath);
            QProcess killall;
            bool killAllFailed = false;
#ifdef Q_OS_WIN
            QString cmd = QLatin1String("taskill /im ") + dPath.fileName()
                    + QLatin1String(" /f /t");
#else
            QString cmd = QLatin1String("killall ") + dPath.fileName()
                    + QLatin1String(" 2> /dev/null");
#endif
            killall.start(cmd);
            if (!killall.waitForStarted()) {
                killAllFailed = true;
            } else {
                killall.closeWriteChannel();
                killall.waitForFinished();
            }
            if (killAllFailed) {
                if (logger)
                    logger->appendError(ErrorMessage::WarningLevel,
                                        ZConfLib::tr("%1 failed to kill other daemons with '%2'.")
                                        .arg(name()).arg(cmd));
                if (DEBUG_ZEROCONF)
                    qDebug() << name() << " had an error trying to kill other daemons with " << cmd;
            }
            QString daemonCmd = daemonPath;
            QStringList daemonArgs;
#ifdef Q_OS_LINUX
            if (QFile::exists("/tmp/mdnsd") && logger)
                logger->appendError(ErrorMessage::WarningLevel,
                                    ZConfLib::tr("%1 detected a file at /tmp/mdnsd, daemon startup will probably fail.")
                                    .arg(name()));
            QString logFile = QString::fromLatin1("/tmp/mdnssd.log");
            static int didFail = 0;
            QFile oldLog(logFile);
            if (didFail > 1 && oldLog.exists()) {
                oldLog.open(QIODevice::ReadOnly);
                if (logger) {
                    QByteArray logBA = oldLog.readAll();
                    logger->appendError(ErrorMessage::NoteLevel,
                                        ZConfLib::tr("%1: log of previous daemon run is: '%2'.\n")
                                        .arg(name())
                                        .arg(QString::fromLatin1(logBA.constData(), logBA.size())));
                    qDebug()<<logBA.size()<<oldLog.error()<<oldLog.errorString();
                }
                oldLog.close();
            }
            if (++didFail > 1)
                daemonArgs << QString::fromLatin1("-debug");
#endif
            if (QProcess::startDetached(daemonCmd, daemonArgs)) {
                QThread::yieldCurrentThread();
                // sleep a bit?
                if (DEBUG_ZEROCONF)
                    qDebug() << name() << " started " << daemonCmd << daemonArgs;
                return true;
            } else {
                this->setError(true, ZConfLib::tr("%1 failed starting embedded daemon at %2.")
                               .arg(name()).arg(daemonPath));
            }
        }
        return false;
    }

    void refDeallocate(DNSServiceRef sdRef)
    {
        embeddedLib::DNSServiceRefDeallocate(sdRef);
    }

    void browserDeallocate(BrowserRef *bRef)
    {
        if (bRef){
            embeddedLib::DNSServiceRefDeallocate(*reinterpret_cast<DNSServiceRef*>(bRef));
            *bRef = 0;
        }
    }

    void stopConnection(ConnectionRef cRef)
    {
        int sock = refSockFD(cRef);
        if (sock>0)
            shutdown(sock, SHUT_RDWR);
    }

    void destroyConnection(ConnectionRef *sdRef)
    {
        if (sdRef) {
            embeddedLib::DNSServiceRefDeallocate(*reinterpret_cast<DNSServiceRef*>(sdRef));
            *sdRef = 0;
        }
    }

    DNSServiceErrorType resolve(ConnectionRef                       cRef,
                                DNSServiceRef                       *sdRef,
                                uint32_t                            interfaceIndex,
                                ZK_IP_Protocol                      /* protocol */,
                                const char                          *name,
                                const char                          *regtype,
                                const char                          *domain,
                                ServiceGatherer                     *gatherer)
    {
        *sdRef = reinterpret_cast<DNSServiceRef>(cRef);
        return embeddedLib::DNSServiceResolve(sdRef, kDNSServiceFlagsShareConnection
                                              | kDNSServiceFlagsTimeout,
                                              interfaceIndex, name, regtype, domain,
                                              &cServiceResolveReply, gatherer);
    }

    DNSServiceErrorType queryRecord(ConnectionRef                       cRef,
                                    DNSServiceRef                       *sdRef,
                                    uint32_t                            interfaceIndex,
                                    const char                          *fullname,
                                    ServiceGatherer                     *gatherer)

    DNSServiceErrorType getAddrInfo(ConnectionRef                    cRef,
                                    DNSServiceRef                    *sdRef,
                                    uint32_t                         interfaceIndex,
                                    DNSServiceProtocol               protocol,
                                    const char                       *hostname,
                                    ServiceGatherer                  *gatherer)
    {
        *sdRef = reinterpret_cast<DNSServiceRef>(cRef);
        return embeddedLib::DNSServiceGetAddrInfo(sdRef, kDNSServiceFlagsShareConnection
                                                  | kDNSServiceFlagsTimeout,
                                                  interfaceIndex, protocol,
                                                  hostname, &cAddrReply, gatherer);
    }

    DNSServiceErrorType reconfirmRecord(ConnectionRef /*cRef*/, uint32_t /*interfaceIndex*/,
                                        const char * /*name*/, const char * /*type*/,
                                        const char * /*domain*/, const char * /*fullname*/)
    {
        // reload and force update with in the callback with
        return kDNSServiceErr_Unsupported;
    }

    DNSServiceErrorType browse(ConnectionRef                       cRef,
                               BrowserRef                          *bRef,
                               uint32_t                            interfaceIndex,
                               const char                          *regtype,
                               const char                          *domain,    /* may be NULL */
                               ServiceBrowserPrivate               *browser)
    {
        DNSServiceRef *sdRef = reinterpret_cast<DNSServiceRef *>(bRef);
        *sdRef = reinterpret_cast<DNSServiceRef>(cRef);
        return embeddedLib::DNSServiceBrowse(sdRef, kDNSServiceFlagsShareConnection
                                             /* | kDNSServiceFlagsSuppressUnusable */,
                                             interfaceIndex, regtype, domain, &cBrowseReply,
                                             browser);
    }

    DNSServiceErrorType getProperty(const char *property, void *result, uint32_t *size)
    {
        return embeddedLib::DNSServiceGetProperty(property, result, size);
    }

    RunLoopStatus processOneEventBlock(ConnectionRef cRef)
    {
        if (embeddedLib::DNSServiceProcessResult(reinterpret_cast<DNSServiceRef>(cRef)) != kDNSServiceErr_NoError)
            return ProcessedError;
        return ProcessedOk;
    }

    DNSServiceErrorType createConnection(MainConnection *, ConnectionRef *sdRef)
    {
        return embeddedLib::DNSServiceCreateConnection(reinterpret_cast<DNSServiceRef*>(sdRef));
    }

    int refSockFD(ConnectionRef sdRef)
    {
        return embeddedLib::DNSServiceRefSockFD(reinterpret_cast<DNSServiceRef>(sdRef));
    }
};

ZConfLib::Ptr ZConfLib::createEmbeddedLib(const QString &daemonPath, const ZConfLib::Ptr &fallback)
{
    return ZConfLib::Ptr(new EmbeddedZConfLib(daemonPath, fallback));
}
} // namespace Internal
} // namespace ZeroConf

#else // no embedded lib

namespace ZeroConf {
namespace Internal {

ZConfLib::Ptr ZConfLib::createEmbeddedLib(const QString &, const ZConfLib::Ptr &fallback)
{
    return fallback;
}

} // namespace Internal
} // namespace ZeroConf
#endif