function saveFile ( name , type , base64Content , key )
{
    if ( typeof key == 'undefined' )
    {
        key = name + '_' + type + '_' + CryptoJS.MD5 ( base64Content ).toString();
    }
    if ( _agnia.context.wallet[_agnia.context.wallet_name].config.remote_storage == true )
    {
        saveRemoteFile ( key , name , type , base64Content );
    }
    else
    {
        saveLocalFile ( key , name , type , base64Content );
    }
    return key;
}

function loadFile ( key , callback )
{
    if ( _agnia.context.wallet[_agnia.context.wallet_name].config.remote_storage == true )
    {
        getRemoteFile ( key , function ( data )
        {
            if ( data.errnum || typeof data.data == 'undefined' )
            {
                callback ( null );
            }
            else
            {
                callback ( data.data );
            }
        } );
    }
    else
    {
        getLocalFile ( key , function ( data )
        {
            if ( data.errnum || typeof data.data == 'undefined' )
            {
                callback ( null );
            }
            else
            {
                callback ( data.data );
            }
        } );
    }
}

function loadFiles ( fileIds , idx , files , callback )
{
    loadFile ( fileIds[idx] , function ( data ) 
    {
        files[fileIds[idx]] = data;
        idx++;
        if ( idx < fileIds.length )
        {
            loadFiles ( fileIds , idx , files , callback )
        }
        else
        {
            callback();
        }
    }
    );

}

function deleteFile ( key )
{
    if ( _agnia.context.wallet[_agnia.context.wallet_name].config.remote_storage == true )
    {
        deleteRemoteFile ( key );
    }
    else
    {
        deleteLocalFile ( key );
    }
}

function saveLocalFile ( file_key , name , type , base64Content , callback )
{
    var storage = {};
    storage[file_key] = _agnia.encrypt ( JSON.stringify ( { name: name , type: type , data: base64Content } ) , _agnia.context.password );
    browser.storage.local.set ( storage );
    if ( typeof callback == 'function' )
    {
        callback ( { errnum: 0 } );
    }
}

function saveRemoteFile ( file_key , name , type , base64Content , callback )
{
    var requestData = {};
    var data = _agnia.asymmetricCrypt ( JSON.stringify ( { name: name , type: type , data: base64Content } ) ,  _agnia.context.wallet[_agnia.context.wallet_name].did )
    requestData['params'] = {};
    requestData['params'] = { 'file_key': file_key , 'data': data };
    requestData['timestamp'] = ( new Date() ).getTime() / 1000;
    postData ( {
        url: saveRemoteFileUrl ,
        method: 'POST',
        data: {
            data: serializeObject ( requestData ),
            proof: serializeObject ( jwt.createRequestProof ( requestData , _agnia.context.wallet[_agnia.context.wallet_name].did ) ),
            did: _agnia.context.wallet[_agnia.context.wallet_name].did.id
        },
        complete: function ( resp , status )
        {
            if ( status == 'success' )
            {
                var data = resp.responseJSON;
                if ( typeof callback == 'function' )
                {

                    if ( typeof data.proof == 'undefined' )
                    {
                        callback ( { errnum: 2 , errstr: getI18n ( 'errors.response_signature_mismatch' ) , returnData: params['returnData'] } );
                        return;
                    }
                    _agnia.getDid ( filesStorageDid , function ( dataDid )
                    {
                        if ( dataDid.errnum != 0 )
                        {
                            callback ( { errnum: 2 } );
                        }
                        else
                        {
                            if ( ! jwt.validateResponseProof ( data.data , data.proof , dataDid.data ) )
                            {
                                callback ( { errnum: 2 , errstr: getI18n ( 'errors.response_signature_mismatch' ) , returnData: params['returnData'] } );
                                return;
                            }
                            callback ( data );
                        }
                    } );
                }
            }
            else
            {
                if ( typeof callback == 'function' )
                {
                    callback ( { errnum: 2 , errstr: getI18n ( 'errors.invalid_data' )  } );
                }
            }
        }
    } );
}

function getLocalFile ( file_key , callback )
{
    browser.storage.local.get ( file_key , function ( data ) 
    {
        if ( typeof data[file_key] != 'undefined' )
        {
            var decrypted = _agnia.decrypt ( data[file_key] , _agnia.context.password );
            if ( decrypted !== null )
            {
                try
                {
                    var parsed = JSON.parse ( decrypted );
                    if ( typeof parsed == 'object' )
                    {
                        callback ( { errnum: 0 , data: parsed } );
                        return;
                    }
                }
                catch
                {
                    callback ( { errnum: 2 , errstr: getI18n ( 'errors.invalid_data' )  } );
                }
            }
        }
        callback ( { errnum: 2 , errstr: getI18n ( 'errors.invalid_data' )  } );
    } );
}

function getRemoteFile ( file_key , callback )
{
    var requestData = {};
    requestData['params'] = { 'file_key': file_key };
    requestData['timestamp'] = ( new Date() ).getTime() / 1000;
    postData ( {
        url: getRemoteFileUrl ,
        method: 'POST',
        data: {
            data: serializeObject ( requestData ),
            proof: serializeObject ( jwt.createRequestProof ( requestData , _agnia.context.wallet[_agnia.context.wallet_name].did ) ),
            did: _agnia.context.wallet[_agnia.context.wallet_name].did.id
        },
        complete: function ( resp , status )
        {
            if ( status == 'success' )
            {
                var data = resp.responseJSON;
                if ( typeof callback == 'function' )
                {
                    if ( typeof data.data != 'undefined' )
                    {
                        if ( typeof data.proof == 'undefined' )
                        {
                            callback ( { errnum: 2 , errstr: getI18n ( 'errors.response_signature_mismatch' ) , returnData: params['returnData'] } );
                            return;
                        }
                        _agnia.getDid ( filesStorageDid , function ( dataDid )
                        {
                            if ( dataDid.errnum != 0 )
                            {
                                callback ( { errnum: 2 } );
                            }
                            else
                            {
                                if ( ! jwt.validateResponseProof ( data.data , data.proof , dataDid.data ) )
                                {
                                    callback ( { errnum: 2 , errstr: getI18n ( 'errors.response_signature_mismatch' ) , returnData: params['returnData'] } );
                                    return;
                                }
                                var decrypted = _agnia.asymmetricDecrypt ( data.data.data , _agnia.context.wallet[_agnia.context.wallet_name].did );
                                if ( decrypted !== null )
                                {
                                    try
                                    {
                                        var parsed = JSON.parse ( decrypted );
                                        if ( typeof parsed == 'object' )
                                        {
                                            callback ( { errnum: 0 , data: parsed } );
                                            return;
                                        }
                                    }
                                    catch
                                    {
                                        callback ( { errnum: 2 , errstr: getI18n ( 'errors.invalid_data' )  } );
                                        return;
                                    }
                                }


                            }
                        } );

                    }
                }
            }
            else
            {
                if ( typeof callback == 'function' )
                {
                    callback ( { errnum: 2 , errstr: getI18n ( 'errors.invalid_data' )  } );
                }
            }
        }
    } );
}

function deleteLocalFile ( file_key , callback )
{
    browser.storage.local.remove ( file_key );
    if ( typeof callback == 'function' )
    {
        callback ( { errnum: 0 } );
    }
}

function deleteRemoteFile ( file_key , callback )
{
    var requestData = {};
    requestData['params'] = { 'file_key': file_key };
    requestData['timestamp'] = ( new Date() ).getTime() / 1000;
    postData ( {
        url: deleteRemoteFileUrl ,
        method: 'POST',
        data: {
            data: serializeObject ( requestData ),
            proof: serializeObject ( jwt.createRequestProof ( requestData , _agnia.context.wallet[_agnia.context.wallet_name].did ) ),
            did: _agnia.context.wallet[_agnia.context.wallet_name].did.id
        },
        complete: function ( resp , status )
        {
            if ( typeof callback == 'function' )
            {
                if ( status == 'success' )
                {
                    var data = resp.responseJSON;
                    if ( typeof data.data != 'undefined' )
                    {

                        if ( typeof data.proof == 'undefined' )
                        {
                            callback ( { errnum: 2 , errstr: getI18n ( 'errors.response_signature_mismatch' ) , returnData: params['returnData'] } );
                            return;
                        }
                        _agnia.getDid ( filesStorageDid , function ( dataDid )
                        {
                            if ( dataDid.errnum != 0 )
                            {
                                callback ( { errnum: 2 } );
                            }
                            else
                            {
                                if ( ! jwt.validateResponseProof ( data.data , data.proof , dataDid.data ) )
                                {
                                    callback ( { errnum: 2 , errstr: getI18n ( 'errors.response_signature_mismatch' ) , returnData: params['returnData'] } );
                                    return;
                                }
                            }
                        } );
                    }
                    callback ( data.data );
                }
                else
                {
                    callback ( { errnum: 2 , errstr: getI18n ( 'errors.server_error' ) } );
                }
            }
        }
    } );
}

function deleteAllRemoteFiles ( callback )
{
    var requestData = {};
    requestData['timestamp'] = ( new Date() ).getTime() / 1000;
    postData ( {
        url: deleteAllRemoteFilesUrl ,
        method: 'POST',
        data: {
            data: serializeObject ( requestData ),
            proof: serializeObject ( jwt.createRequestProof ( requestData , _agnia.context.wallet[_agnia.context.wallet_name].did ) ),
            did: _agnia.context.wallet[_agnia.context.wallet_name].did.id
        },
        complete: function ( resp , status )
        {
            if ( typeof callback == 'function' )
            {
                if ( status == 'success' )
                {
                    var data = resp.responseJSON;
                    if ( typeof data.data != 'undefined' )
                    {
                        if ( typeof data.proof == 'undefined' )
                        {
                            callback ( { errnum: 2 , errstr: getI18n ( 'errors.response_signature_mismatch' ) , returnData: params['returnData'] } );
                            return;
                        }
                        _agnia.getDid ( filesStorageDid , function ( dataDid )
                        {
                            if ( dataDid.errnum != 0 )
                            {
                                callback ( { errnum: 2 } );
                            }
                            else
                            {
                                if ( ! jwt.validateResponseProof ( data.data , data.proof , dataDid.data ) )
                                {
                                    callback ( { errnum: 2 , errstr: getI18n ( 'errors.response_signature_mismatch' ) , returnData: params['returnData'] } );
                                    return;
                                }
                            }
                        } );
                    }
                    callback ( data.data );
                }
                else
                {
                    callback ( { errnum: 2 , errstr: getI18n ( 'errors.server_error' ) } );
                }
            }
        }
    } );
}

function uploadFile ( file_key , callback )
{
    getLocalFile ( file_key , function ( data )
    {
        if ( data != null && data.data != null )
        {
            saveRemoteFile ( file_key , data.data.name , data.data.type , data.data.data , function ( data )
            {
                if ( data.errnum != 0 )
                {
                    callback ( { errnum: 2 , returnData: { file_key: file_key} } );
                }
                else
                {
                    callback ( { errnum: 0 , returnData: { file_key: file_key} } );
                }
            } );
        }
        else
        {
            callback ( { errnum: 1 , returnData: { file_key: file_key} } );
        }
    } );
}

function downloadFile ( file_key , callback )
{
    getRemoteFile ( file_key , function ( data )
    {
        if ( data != null && data.data != null )
        {
            saveLocalFile ( file_key , data.data.name , data.data.type , data.data.data );
            callback ( { errnum: 0 , returnData: { file_key: file_key} } );
        }
        else
        {
            callback ( { errnum: 1 , returnData: { file_key: file_key} } );
        }
    } );
}

function b64toBlob ( b64Data , contentType='' , sliceSize=512 )
{
    const byteCharacters = atob(b64Data);
    const byteArrays = [];
  
    for (let offset = 0; offset < byteCharacters.length; offset += sliceSize) {
      const slice = byteCharacters.slice(offset, offset + sliceSize);
  
      const byteNumbers = new Array(slice.length);
      for (let i = 0; i < slice.length; i++) {
        byteNumbers[i] = slice.charCodeAt(i);
      }
  
      const byteArray = new Uint8Array(byteNumbers);
      byteArrays.push(byteArray);
    }
  
    const blob = new Blob(byteArrays, {type: contentType});
    return blob;
}

function dataURItoBlob ( dataURI )
{
    var partes = dataURI.split ( ',' );
    return b64toBlob ( partes[1] , partes[0].substring ( 'data:base64;'.length ) )
}

function blobToData ( blob )
{
    return new Promise ( ( resolve ) =>
    {
        const reader = new FileReader()
        reader.onloadend = () => resolve ( reader.result )
        reader.readAsDataURL ( blob )
    })
  }