/*
 * Copyright (C) 2016 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.mtp;

import android.content.Context;
import android.mtp.MtpObjectInfo;
import android.os.ParcelFileDescriptor;
import android.system.ErrnoException;
import android.system.Os;
import android.system.OsConstants;

import com.android.internal.util.Preconditions;

import java.io.File;
import java.io.IOException;

class MtpFileWriter implements AutoCloseable {
    final ParcelFileDescriptor mCacheFd;
    final String mDocumentId;
    boolean mDirty;

    MtpFileWriter(Context context, String documentId) throws IOException {
        mDocumentId = documentId;
        mDirty = false;
        final File tempFile = File.createTempFile("mtp", "tmp", context.getCacheDir());
        mCacheFd = ParcelFileDescriptor.open(
                tempFile,
                ParcelFileDescriptor.MODE_READ_WRITE |
                ParcelFileDescriptor.MODE_TRUNCATE |
                ParcelFileDescriptor.MODE_CREATE);
        tempFile.delete();
    }

    String getDocumentId() {
        return mDocumentId;
    }

    int write(long offset, int size, byte[] bytes) throws IOException, ErrnoException {
        Preconditions.checkArgumentNonnegative(offset, "offset");
        Preconditions.checkArgumentNonnegative(size, "size");
        Preconditions.checkArgument(size <= bytes.length);
        if (size == 0) {
            return 0;
        }
        mDirty = true;
        Os.lseek(mCacheFd.getFileDescriptor(), offset, OsConstants.SEEK_SET);
        return Os.write(mCacheFd.getFileDescriptor(), bytes, 0, size);
    }

    void flush(MtpManager manager, MtpDatabase database, int[] operationsSupported)
            throws IOException, ErrnoException {
        // Skip unnecessary flush.
        if (!mDirty) {
            return;
        }

        // Get the placeholder object info.
        final Identifier identifier = database.createIdentifier(mDocumentId);
        final MtpObjectInfo placeholderObjectInfo =
                manager.getObjectInfo(identifier.mDeviceId, identifier.mObjectHandle);

        // Delete the target object info if it already exists (as a placeholder).
        manager.deleteDocument(identifier.mDeviceId, identifier.mObjectHandle);

        // Create the target object info with a correct file size and upload the file.
        final long size = Os.lseek(mCacheFd.getFileDescriptor(), 0, OsConstants.SEEK_END);
        final MtpObjectInfo targetObjectInfo = new MtpObjectInfo.Builder(placeholderObjectInfo)
                .setCompressedSize(size)
                .build();

        Os.lseek(mCacheFd.getFileDescriptor(), 0, OsConstants.SEEK_SET);
        final int newObjectHandle = manager.createDocument(
                identifier.mDeviceId, targetObjectInfo, mCacheFd);

        final MtpObjectInfo newObjectInfo = manager.getObjectInfo(
                identifier.mDeviceId, newObjectHandle);
        final Identifier parentIdentifier =
                database.getParentIdentifier(identifier.mDocumentId);
        database.updateObject(
                identifier.mDocumentId,
                identifier.mDeviceId,
                parentIdentifier.mDocumentId,
                operationsSupported,
                newObjectInfo,
                size);

        mDirty = false;
    }

    @Override
    public void close() throws IOException {
        mCacheFd.close();
    }
}