contract ExternalStorage is Ownable {
    address public addrMainContract;
    //address public owner;
    uint public constant maxRecords = 100;

    struct RP {
        uint256 ID ;
        string name ;
        string location ;
        string description ;
        string site;
        string googlemap;
        string pathToPhoto;
        bool isWork ;
    }
    
    RP[] public rps;                                     //patrimonial settlement (родовые поселения)



    struct User {
        string login;
        string name;
        uint256 RPID;
        bool isRPAdmin;
        bool isUser;
        mapping(uint256 => uint256) projetcsDep;        // вложения в проекты: ProjectID => Amount
    }
    mapping (address => User) public users;
    mapping (string => address) public userAddress;             //string = login

    string[] public userArray;                                  // Array of login users
    mapping (string => bool) isUserInArray;                      // string = login - mapping for quick detect users in userArray
    
    struct Project {
        uint256 ID;
        string name;
        string description;
        uint256 RPID;
        address admin;
        uint256 profit;
        string site;
        string pathToPhoto;
        bool isWork;
    }
    Project[] public projects;

    constructor(address _owner) public {
        owner = _owner;
        addrMainContract = msg.sender;
    }
  
    modifier onlyOwner() {
        // only MainContract contract
        require(msg.sender == owner);
        _;
    }
    
    modifier onlyMainContract() {
        // only MainContract contract
        require(msg.sender == addrMainContract);
        _;
    }     
     
        modifier isRPAdmin (address _addr) {
        require(users[_addr].isRPAdmin == true, "Only for project admins");
        _;
    }

    /**
     * internal functions 
     */
    function getNewRPID() internal view returns (uint256) {
        return rps.length +1;
    }
    
        function getNewProjectID() internal view returns (uint256) {
        return projects.length +1;
    }
    
    function getRPID(address _addr) internal view returns (uint256) {
        return users[_addr].RPID;
    }

    function getRPName(uint256 _RPID) internal view returns (string memory) {
        return rps[_RPID-1].name;
    }

    /**
     * Migrate functions
     */
    function changeAddrMainContract(address _newAddr) public onlyOwner {
        addrMainContract = _newAddr;
    }
    
    
    /**
     * Base functions
     */
    function isRPAdminF (address _addr) public view returns(bool) {
        return users[_addr].isRPAdmin;
        
    }

    function addRP(string memory _name, string memory _location, string memory _description) public onlyOwner {
        //get new ID
        uint256 myID = getNewRPID();
        
        RP memory newRP = RP({
            ID: myID,
            name: _name,
            location: _location,
            description: _description,
            site: '',
            googlemap: '',
            pathToPhoto: '',
            isWork: true
        });
        rps.push(newRP);
    }
    
    function addRPAdmin(uint256 _RPID, string memory _login, string memory _name, address _addr) public onlyOwner {
        require (_addr != address(0),"Only real address");
        User memory newUser = User ({
            login: _login,
            name: _name,
            RPID: _RPID,
            isRPAdmin: true,
            isUser: true
        });
        users[_addr] = newUser;
        if (isUserInArray[_login] == false) userArray.push(_login);
        userAddress[_login] = _addr;
        isUserInArray[_login] = true;
    }

    function cleareUser(string memory _login) public onlyOwner {
        address myAddr = userAddress[_login];
        User memory clearUser = User ({
            login: "",
            name: "",
            RPID: 0,
            isRPAdmin: false,
            isUser:false
        });
        users[myAddr] = clearUser;
        userAddress[_login]=address(0);
    }

    function addProject(string memory _name, string memory _description) public isRPAdmin(msg.sender) {
        uint256 newProjectID = getNewProjectID();
        uint256 myRPID = getRPID(address(msg.sender));
        Project memory newProject = Project ({
            ID: newProjectID,
            name: _name,
            description: _description,
            RPID: myRPID,
            admin: address(msg.sender),
            profit: 0,
            pathToPhoto: '',
            site: '',
            isWork: true
        });
        projects.push(newProject);
    }
    
    function editProjectProfit(uint256 _projectID, uint256 _newProfit) public isRPAdmin(msg.sender) {
        projects[_projectID-1].profit = _newProfit;
    }

    function editProjectPhoto(uint256 _projectID, string memory _newPhoto) public isRPAdmin(msg.sender) {
        projects[_projectID-1].pathToPhoto = _newPhoto;
    }

    function editProjectSite(uint256 _projectID, string memory _newSite) public isRPAdmin(msg.sender) {
        projects[_projectID-1].site = _newSite;
    }

    function addUser(string memory _login, string memory _name, address _addr) public isRPAdmin(msg.sender) {
        require (_addr != address(0),"Only real address");
        require(userAddress[_login] == address(0),"This login allready in base");
        require(users[_addr].isUser == false,"This address allready in base");
        User memory newUser = User ({
            login: _login,
            name: _name,
            RPID: getRPID(msg.sender),
            isRPAdmin: false,
            isUser:true
        });
        users[_addr] = newUser;
        if (isUserInArray[_login] == false) userArray.push(_login);
        userAddress[_login] = _addr;
        isUserInArray[_login] = true;

    }

    function changeUserLogin (string memory _oldLogin, string memory _newLogin) public isRPAdmin(msg.sender) {
        require(isUserInArray[_oldLogin] == true, 'User not found');
        uint32 x = 0;
        address myUserAddress;
        while (x < userArray.length) {
            if (keccak256(bytes(userArray[x])) == keccak256(bytes(_oldLogin))){
                myUserAddress = userAddress[_oldLogin];
                userArray[x] = _newLogin;
                users[myUserAddress].login = _newLogin;
                userAddress[_oldLogin] = address(0);
                userAddress[_newLogin] = myUserAddress;
                isUserInArray[_oldLogin] = false;
                isUserInArray[_newLogin] = true;
            }
            x++;
        }
    }
    function changeUserName (address _userAddress, string memory _newName) public isRPAdmin (msg.sender) {
        require (_userAddress != address(0), 'invalid address');
        users[_userAddress].name = _newName;
    }

     function getUserCount() public view returns(uint256) {
         return userArray.length;
     }

     function getRPCount() public view returns(uint256) {
         return rps.length;
     }

     function getProjectCount() public view returns(uint256) {
         return projects.length;
     }
     
     function getUserDeposit(address _addr, uint256 _projectID) public view returns (uint256) {
         return users[_addr].projetcsDep[_projectID];
     }


 /**
     * Main admin functions for patrimonial settlement control
     */


    function editRPName(uint256 _RPID, string memory _newName) public onlyOwner {
        rps[_RPID-1].name = _newName;
    }
    
    function editRPLocation(uint256 _RPID, string memory _newLocation) public onlyOwner {
        rps[_RPID-1].location = _newLocation;
    }
    
    function editRPDescription(uint256 _RPID, string memory _newDescription) public onlyOwner {
        rps[_RPID-1].description = _newDescription;
    }    

    function editRPsite(uint256 _RPID, string memory _newSite) public onlyOwner {
        rps[_RPID-1].site = _newSite;
    }    
    
    function editRPGoogleMap(uint256 _RPID, string memory _newGoogleMap) public onlyOwner {
        rps[_RPID-1].googlemap = _newGoogleMap;
    }    

    function editRPPhoto(uint256 _RPID, string memory _pathToPhoto) public onlyOwner {
        rps[_RPID-1].pathToPhoto = _pathToPhoto;
    }    

    function lockRP(uint256 _RPID) public onlyOwner {
        rps[_RPID-1].isWork = false;
    }
    
    function unlockRP(uint256 _RPID) public onlyOwner {
        rps[_RPID-1].isWork = true;
    }



    /**
     * Main admin functions for projects control
     */

    function editNameProject(uint256 _projectID, string memory _newName) public onlyOwner {
        projects[_projectID-1].name = _newName;
    }

    function editDescriptionProject(uint256 _projectID, string memory _newDescription) public onlyOwner {
        projects[_projectID-1].description = _newDescription;
    }
    
    
    function editAdminProject(uint256 _projectID, address _newAdmin) public onlyOwner {
        projects[_projectID-1].admin = _newAdmin;
    }
    
    function killProject(uint256 _projectID) public onlyOwner {
        projects[_projectID-1].RPID=0;
        projects[_projectID-1].name='';
        projects[_projectID-1].description='';
        projects[_projectID-1].admin=address(0);
        projects[_projectID-1].pathToPhoto='';
        projects[_projectID-1].site='';
        projects[_projectID-1].profit=0;
        projects[_projectID-1].isWork = false;
    }


    /**
     * RPadmin functions for projects control
     */


    function lockProject(uint256 _projectID) public isRPAdmin(msg.sender) {
        require (projects[_projectID-1].admin == msg.sender,"Is not your project");
        projects[_projectID-1].isWork = false;
    }
    
    function unlockProject(uint256 _projectID) public isRPAdmin(msg.sender) {
        require (projects[_projectID-1].admin == msg.sender,"Is not your project");
        projects[_projectID-1].isWork = true;
    }

    /**
     * RPadmin functions for deposit control
     */
    function addDeposit (address _investor, uint256 _projectID, uint256 _value, address _RPAdmin) public  onlyMainContract {
        require(projects[_projectID-1].admin == _RPAdmin, "Is not your project");
        users[_investor].projetcsDep[_projectID] += _value; 
    }    
     
    function revokeDeposit (address _investor, uint256 _projectID, uint256 _value, address _RPAdmin) public onlyMainContract {
        require (users[_investor].projetcsDep[_projectID] >= _value,"User deposit is smaller then you want revoke");
        require(projects[_projectID-1].admin == _RPAdmin, "Is not your project");
        users[_investor].projetcsDep[_projectID] -= _value; 
    }  


    /**
     * User functions
     * showDeposit - возвращает вкладчиков определенного проекта
     * showRPS - возвращает список поселений
     * showProjects - возвращает проекты определенного поселения
     * showUsers - отображает пользователей
     */ 



        function showUsers(uint256 _RPID) public view returns (bytes32[maxRecords] memory, bytes32[maxRecords] memory){
            //RPID = 0 - отображает всех пользователей
            //RPID > 0 - отображает пользователей определенного поселения
            uint32 x = 0;
            uint32 count = 0;
            bytes32[maxRecords] memory myRPSNameArray;
            bytes32[maxRecords] memory myUserNameArray;
            address myUserAddress;
            while (x < userArray.length) {
                myUserAddress = userAddress[userArray[x]];
                if (myUserAddress != address(0) && users[myUserAddress].isUser == true){
                    if (_RPID > 0) {
                        if (_RPID == users[myUserAddress].RPID){
                            myUserNameArray[count] = stringToBytes32(users[myUserAddress].name);
                            myRPSNameArray[count] = stringToBytes32(getRPName(users[myUserAddress].RPID));
                            count++;
                        }
                    } else {
                        myUserNameArray[count] = stringToBytes32(users[myUserAddress].name);
                        myRPSNameArray[count] = stringToBytes32(getRPName(users[myUserAddress].RPID));
                        count++;
                        
                    }
                }
                x++;
            }
            return (myUserNameArray, myRPSNameArray);
        }



        function showRPS() public view returns (bytes32[maxRecords] memory, uint256[maxRecords] memory){
            uint32 x = 0;
            uint32 count = 0;
            bytes32[maxRecords] memory myRPSNameArray;
            uint256[maxRecords] memory myRPSIDArray;
            while (x < rps.length) {
                if (rps[x].isWork == true) {
                    myRPSNameArray[count] = stringToBytes32(rps[x].name);
                    myRPSIDArray[count] = rps[x].ID;
                    count++;
                }
                x++;
            }
            return (myRPSNameArray, myRPSIDArray);
        }


        function showProjects(uint256 _RPID) public view returns (bytes32[maxRecords] memory, uint256[maxRecords] memory){
            uint32 x = 0;
            uint32 count = 0;
            bytes32[maxRecords] memory myProjectNameArray;
            uint256[maxRecords] memory myProjectIDArray;
            while (x < projects.length) {
                if (projects[x].RPID == _RPID && projects[x].isWork == true) {
                    myProjectNameArray[count] = stringToBytes32(projects[x].name);
                    myProjectIDArray[count] = projects[x].ID;
                    count++;
                }
                x++;
            }
            return (myProjectNameArray, myProjectIDArray);
        }

        function showDeposit(uint256 _projectID) public view returns (address[maxRecords] memory, uint256[maxRecords] memory, bytes32[maxRecords] memory, bytes32[maxRecords] memory){
        bytes32[maxRecords] memory myFIOArray;
        bytes32[maxRecords] memory myRPSNameArray;
        address[maxRecords] memory myAdresses;
        uint256[maxRecords] memory myDepo;
        string memory myCurrentLogin;
        address myCurrentAddress;
        uint256 myCurrentDeposit;
        uint32 x = 0;
        uint32 count = 0;
        
        while( x < userArray.length)
            {
                myCurrentLogin = userArray[x]; 
                myCurrentAddress = userAddress[myCurrentLogin];
                myCurrentDeposit = getUserDeposit(myCurrentAddress, _projectID); 
                if (myCurrentDeposit > 0) {
                    myAdresses[count] =myCurrentAddress;
                    myDepo[count] = myCurrentDeposit;
                    myFIOArray[count] = stringToBytes32(users[myCurrentAddress].name); 
                    myRPSNameArray[count] = stringToBytes32(getRPName(users[myCurrentAddress].RPID));
                    count++;
                }
                x++;
            }
        
        return (myAdresses, myDepo, myFIOArray, myRPSNameArray);
    }

/* function test(string memory source) public view returns (bytes32[maxRecords] memory) {
    bytes32[maxRecords] memory myArray;
    myArray[0] = stringToBytes32(source);
    return myArray;
} */


function stringToBytes32(string memory source) internal pure returns (bytes32 result) {
    bytes memory tempEmptyStringTest = bytes(source);
    if (tempEmptyStringTest.length == 0) {
        return 0x0;
    }

    assembly {
        result := mload(add(source, 32))
    }
}



    function() external payable {
        // The token contract don`t receive ether
        revert();
    }  
}
function isRPAdminF (address _addr) public view returns(bool) {
    return users[_addr].isRPAdmin;
        
}

/**
 * Base functions
 */