//SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.13;

import {IStartVoteAndImplement} from "../interface/IVotingIntegration.sol";
import {IVotingContract} from "../../votingContractStandard/IVotingContract.sol";
import {LegitInstanceHash, AssignedContractPrimitive} from "../primitives/AssignedContractPrimitive.sol";
import { Instance, InstanceInfoPrimitive } from "../primitives/InstanceInfoPrimitive.sol";
import {IImplementResult} from "../../extensions/interfaces/IImplementResult.sol";


abstract contract StartVoteAndImplementOnlyCallbackImplRemoteMinml is
IStartVoteAndImplement,
LegitInstanceHash,
AssignedContractPrimitive,
InstanceInfoPrimitive
{

    function start(bytes memory votingParams, bytes calldata callback) 
    external 
    override(IStartVoteAndImplement){
        _beforeStart(votingParams);
        bytes4 selector = bytes4(callback[0:4]);
        if (!AssignedContractPrimitive._isVotableFunction(selector)){
            revert AssignedContractPrimitive.IsNotVotableFunction(selector);
        }
        address votingContract = assignedContract[selector];
        uint256 identifier = IVotingContract(votingContract).start(votingParams, callback);
        instances.push(Instance({
            identifier: identifier,
            votingContract: votingContract
        }));
        bytes32 instanceHash = LegitInstanceHash._getInstanceHash(assignedContract[selector], identifier);
        LegitInstanceHash._isLegitInstanceHash[instanceHash] = true;
        
    }

    
    function vote(uint256 identifier, bytes calldata votingData) 
    external 
    override(IStartVoteAndImplement){
        _beforeVote(identifier);
        IVotingContract(instances[identifier].votingContract).vote(
            instances[identifier].identifier,
            votingData);
    }

    function implement(uint256 identifier, bytes calldata callback)
    external 
    payable
    override(IStartVoteAndImplement) 
    {
        IImplementResult(instances[identifier].votingContract).implement(
                instances[identifier].identifier,
                callback);
        
    }

    function _beforeStart(bytes memory votingParams) virtual internal {}

    function _beforeVote(uint256 identifier) virtual internal {}
}

abstract contract StartVoteAndImplementOnlyCallbackImplRemoteHooks is 
IStartVoteAndImplement,
LegitInstanceHash,
AssignedContractPrimitive,
InstanceInfoPrimitive
{

    function start(bytes memory votingParams, bytes calldata callback) 
    external 
    override(IStartVoteAndImplement){
        _beforeStart(votingParams, callback);
        bytes4 selector = bytes4(callback[0:4]);
        if (!AssignedContractPrimitive._isVotableFunction(selector)){
            revert AssignedContractPrimitive.IsNotVotableFunction(selector);
        }
        address votingContract = assignedContract[selector];
        uint256 identifier = IVotingContract(votingContract).start(votingParams, callback);
        instances.push(Instance({
            identifier: identifier,
            votingContract: votingContract
        }));
        bytes32 instanceHash = LegitInstanceHash._getInstanceHash(assignedContract[selector], identifier);
        LegitInstanceHash._isLegitInstanceHash[instanceHash] = true;
        
        _afterStart(instances.length - 1, votingParams, callback);
    }

    
    function vote(uint256 identifier, bytes calldata votingData) 
    external 
    override(IStartVoteAndImplement){
        _beforeVote(identifier, votingData);
        uint256 status = IVotingContract(instances[identifier].votingContract).vote(
            instances[identifier].identifier,
            _modifyVotingData(identifier, votingData));
        _afterVote(identifier, status, votingData);
    }

    function implement(uint256 identifier, bytes calldata callback)
    external 
    payable
    override(IStartVoteAndImplement) 
    {
        _beforeImplement(identifier);
        IImplementResult.Response _response = IImplementResult(instances[identifier].votingContract).implement(
                instances[identifier].identifier,
                callback);
        _afterImplement(identifier, _response==IImplementResult.Response.successful);
        
    }

    function _beforeStart(bytes memory votingParams, bytes calldata callback) virtual internal {}

    function _afterStart(uint256 identifier, bytes memory votingParams, bytes calldata callback) virtual internal {}
    
    function _beforeVote(uint256 identifier, bytes calldata votingData) virtual internal {}

    function _modifyVotingData(uint256 identifier, bytes calldata votingData) virtual internal returns(bytes memory newVotingData){ return votingData;}

    function _afterVote(uint256 identifier, uint256 status, bytes calldata votingData) virtual internal {}

    function _beforeImplement(uint256 identifier) virtual internal {}
    
    function _afterImplement(uint256 identifier, bool responseFlag) virtual internal {}    

}


abstract contract StartVoteAndImplementHybridVotingImplRemoteMinml is
IStartVoteAndImplement,
LegitInstanceHash,
AssignedContractPrimitive,
InstanceInfoPrimitive 
{
    
    address public votingContract;

    function start(bytes memory votingParams, bytes calldata callback) 
    external 
    override(IStartVoteAndImplement){
         _beforeStart(votingParams);
        address _votingContract;
        uint256 identifier;
        if (callback.length<4){
            _votingContract = votingContract;
            identifier = IVotingContract(_votingContract).start(votingParams, callback);
        } else {
            bytes4 selector = bytes4(callback[0:4]);
            if (!AssignedContractPrimitive._isVotableFunction(selector)){
                revert AssignedContractPrimitive.IsNotVotableFunction(selector);
            }
            _votingContract = assignedContract[selector];
            identifier = IVotingContract(_votingContract).start(votingParams, callback);
            bytes32 instanceHash = LegitInstanceHash._getInstanceHash(assignedContract[selector], identifier);
            LegitInstanceHash._isLegitInstanceHash[instanceHash] = true;
        
        }
        instances.push(Instance({
            identifier: identifier,
            votingContract: _votingContract
        }));
    }

    
    function vote(uint256 identifier, bytes calldata votingData) 
    external 
    override(IStartVoteAndImplement){
        _beforeVote(identifier);
        IVotingContract(instances[identifier].votingContract).vote(
            instances[identifier].identifier,
            votingData);
    }

    function implement(uint256 identifier, bytes calldata callback)
    external 
    payable
    override(IStartVoteAndImplement) 
    {
        _beforeImplement(identifier);
        IImplementResult(instances[identifier].votingContract).implement(
                instances[identifier].identifier,
                callback);
        
    }

    function _beforeStart(bytes memory votingParams) virtual internal {}

    function _beforeVote(uint256 identifier) virtual internal {}

    function _beforeImplement(uint256 identifier) virtual internal {}
    
}

abstract contract StartVoteAndImplementHybridVotingImplRemoteHooks is 
IStartVoteAndImplement,
LegitInstanceHash,
AssignedContractPrimitive,
InstanceInfoPrimitive
{
    function start(bytes memory votingParams, bytes calldata callback) 
    external 
    override(IStartVoteAndImplement){
        _beforeStart(votingParams, callback);
        address _votingContract;
        uint256 identifier;
        if (callback.length<4){
            _votingContract = _getSimpleVotingContract(callback);
            identifier = IVotingContract(_votingContract).start(votingParams, callback);
        } else {
            bytes4 selector = bytes4(callback[0:4]);
            if (!AssignedContractPrimitive._isVotableFunction(selector)){
                revert AssignedContractPrimitive.IsNotVotableFunction(selector);
            }
            _votingContract = assignedContract[selector];
            identifier = IVotingContract(_votingContract).start(votingParams, callback);
            bytes32 instanceHash = LegitInstanceHash._getInstanceHash(assignedContract[selector], identifier);
            LegitInstanceHash._isLegitInstanceHash[instanceHash] = true;
        }
        instances.push(Instance({
            identifier: identifier,
            votingContract: _votingContract
        }));
        _afterStart(instances.length - 1, votingParams, callback);
    }

    
    function vote(uint256 identifier, bytes calldata votingData) 
    external 
    override(IStartVoteAndImplement){
        _beforeVote(identifier, votingData);
        uint256 status = IVotingContract(instances[identifier].votingContract).vote(
            instances[identifier].identifier,
            _modifyVotingData(identifier, votingData));
        _afterVote(identifier, status, votingData);
    }

    function implement(uint256 identifier, bytes calldata callback)
    external
    payable 
    override(IStartVoteAndImplement) 
    {
        _beforeImplement(identifier);
        bytes memory data = abi.encodeWithSelector(
                IImplementResult.implement.selector, 
                instances[identifier].identifier,
                callback);
        (bool success, bytes memory responsedata) = instances[identifier].votingContract.call{value: msg.value}(data);
       
        _afterImplement(identifier, success);
        
    }

    event WhatsNow(bool success, bytes response);

    

    function _beforeStart(bytes memory votingParams, bytes calldata callback) virtual internal {}

    function _afterStart(uint256 identifier, bytes memory votingParams, bytes calldata callback) virtual internal {}
    
    function _getSimpleVotingContract(bytes calldata callback) virtual internal view returns(address) {}

    function _beforeVote(uint256 identifier, bytes calldata votingData) virtual internal {}

    function _modifyVotingData(uint256 identifier, bytes calldata votingData) virtual internal returns(bytes memory newVotingData){ return votingData;}

    function _afterVote(uint256 identifier, uint256 status, bytes calldata votingData) virtual internal {}

    function _beforeImplement(uint256 identifier) virtual internal {}
    
    function _afterImplement(uint256 identifier, bool responseFlag) virtual internal {}    

}
[{"inputs":[{"internalType":"address","name":"votingContractRegistry","type":"address"},{"internalType":"bytes5[]","name":"flagAndSelectors","type":"bytes5[]"},{"internalType":"address[]","name":"votingContracts","type":"address[]"},{"internalType":"uint256[]","name":"minDurations","type":"uint256[]"},{"internalType":"uint256[]","name":"minQuorums","type":"uint256[]"},{"internalType":"bool[]","name":"badgeWeightedVote","type":"bool[]"},{"internalType":"bytes32","name":"hashedBadgeBytecode","type":"bytes32"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"uint256","name":"mainBadge","type":"uint256"},{"internalType":"uint256","name":"numberOfBadges","type":"uint256"}],"name":"BadgeDoesntExist","type":"error"},{"inputs":[{"internalType":"address","name":"imposter","type":"address"}],"name":"IsNotImplementer","type":"error"},{"inputs":[{"internalType":"bytes4","name":"selector","type":"bytes4"}],"name":"IsNotVotableFunction","type":"error"},{"inputs":[{"internalType":"address","name":"implementer","type":"address"}],"name":"OnlyVoteImplementer","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"index","type":"uint256"},{"indexed":false,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"bytes","name":"target","type":"bytes"}],"name":"VotingInstanceStarted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"success","type":"bool"},{"indexed":false,"internalType":"bytes","name":"response","type":"bytes"}],"name":"WhatsNow","type":"event"},{"inputs":[],"name":"VOTING_REGISTRY","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"votingContract","type":"address"}],"name":"addSimpleVotingContract","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"analytics","outputs":[{"internalType":"uint256","name":"numberOfInstances","type":"uint256"},{"internalType":"uint256","name":"numberOfVotes","type":"uint256"},{"internalType":"uint256","name":"numberOfImplementations","type":"uint256"},{"internalType":"uint24","name":"numberOfSimpleVotingContracts","type":"uint24"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approveERC20Token","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"approveNFT","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"badges","outputs":[{"internalType":"contract IPlaygroundVotingBadge","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"index","type":"uint256"},{"internalType":"bytes4","name":"selector","type":"bytes4"},{"internalType":"address","name":"votingContract","type":"address"},{"internalType":"address","name":"minter","type":"address"}],"name":"calculateId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes4","name":"selector","type":"bytes4"},{"internalType":"address","name":"newVotingContract","type":"address"}],"name":"changeAssignedContract","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"by","type":"uint256"}],"name":"changeCounter","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"selector","type":"bytes4"},{"internalType":"uint256","name":"minDuration","type":"uint256"},{"internalType":"uint256","name":"minQuorum","type":"uint256"},{"internalType":"address","name":"token","type":"address"}],"name":"changeMetaParameters","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"enum Operation","name":"newOperation","type":"uint8"}],"name":"changeOperation","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"counter","outputs":[{"internalType":"uint256","name":"counter","type":"uint256"},{"internalType":"enum Operation","name":"operation","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"salt","type":"bytes32"},{"internalType":"bytes","name":"bytecode","type":"bytes"},{"internalType":"address","name":"badger","type":"address"}],"name":"deployNewBadge","outputs":[{"internalType":"address","name":"deployedContract","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"salt","type":"bytes32"},{"internalType":"bytes","name":"bytecode","type":"bytes"}],"name":"deployNewContract","outputs":[{"internalType":"address","name":"deployedContract","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"deployedContracts","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"donationsBy","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"name":"fixedVotingContract","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes4","name":"selector","type":"bytes4"}],"name":"getAssignedContract","outputs":[{"internalType":"address","name":"_assignedContract","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"office","type":"string"}],"name":"getIncumbentFromOffice","outputs":[{"internalType":"address","name":"incumbent","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"identifier","type":"uint256"},{"internalType":"bytes","name":"callback","type":"bytes"}],"name":"implement","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"instances","outputs":[{"internalType":"uint256","name":"identifier","type":"uint256"},{"internalType":"address","name":"votingContract","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"minXpToStartAnything","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"name":"minXpToStartThisFunction","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"office","type":"string"},{"internalType":"address","name":"_newIncumbent","type":"address"}],"name":"newIncumbent","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"nftAndBadgesInfo","outputs":[{"internalType":"uint256","name":"mainBadge","type":"uint256"},{"internalType":"bool","name":"acceptingNFTs","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"offices","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"address","name":"from","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"onERC721Received","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"sendERC20Token","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"sendNFT","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address payable","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"sendNativeToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"accept","type":"bool"}],"name":"setAcceptingNFTs","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"newThreshold","type":"uint256"}],"name":"setEnableTradingThreshold","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"newMainBadge","type":"uint256"}],"name":"setMainBadge","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"newXP","type":"uint256"}],"name":"setMinXpToStartAnything","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"selector","type":"bytes4"},{"internalType":"uint256","name":"newXP","type":"uint256"}],"name":"setMinXpToStartThisFunction","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"enable","type":"bool"}],"name":"setTradingEnabledGlobally","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint24","name":"","type":"uint24"}],"name":"simpleVotingContract","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"votingParams","type":"bytes"},{"internalType":"bytes","name":"callback","type":"bytes"}],"name":"start","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"identifier","type":"uint256"},{"internalType":"bytes","name":"votingData","type":"bytes"}],"name":"vote","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"name":"votingMetaParams","outputs":[{"internalType":"uint256","name":"minDuration","type":"uint256"},{"internalType":"uint256","name":"minQuorum","type":"uint256"},{"internalType":"address","name":"token","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"contractAddress","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"wildCard","outputs":[],"stateMutability":"payable","type":"function"},{"stateMutability":"payable","type":"receive"}]