// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'dart:async';
import 'dart:io';
import 'dart:typed_data';

import 'package:mojo/core.dart';

// Helper class to drain the contents of a mojo data pipe to a file.
class PipeToFile {
  MojoDataPipeConsumer _consumer;
  MojoEventSubscription _events;
  IOSink _outputStream;

  PipeToFile(this._consumer, String outputPath) {
    _events = new MojoEventSubscription(_consumer.handle);
    _outputStream = new File(outputPath).openWrite();
  }

  Future<int> _doRead() async {
    ByteData thisRead = _consumer.beginRead();
    if (thisRead == null) {
      throw 'Data pipe beginRead failed: ${_consumer.status}';
    }
    // TODO(mpcomplete): Should I worry about the _eventStream listen callback
    // being invoked again before this completes?
    await _outputStream.add(thisRead.buffer.asUint8List());
    return _consumer.endRead(thisRead.lengthInBytes);
  }

  Future<int> drain() {
    Completer<int> completer = new Completer();
    _events.subscribe((int signal) {
      (() async {
        if (MojoHandleSignals.isReadable(signal)) {
          int result = await _doRead();
          if (result != MojoResult.kOk) {
            _events.close();
            _events = null;
            _outputStream.close();
            completer.complete(result);
          } else {
            _events.enableReadEvents();
          }
        } else if (MojoHandleSignals.isPeerClosed(signal)) {
          _events.close();
          _events = null;
          _outputStream.close();
          completer.complete(MojoResult.kOk);
        } else {
          throw 'Unexpected handle event: ${MojoHandleSignals.string(signal)}';
        }
      })();
    });
    return completer.future;
  }

  static Future<int> copyToFile(MojoDataPipeConsumer consumer, String outputPath) {
    PipeToFile drainer = new PipeToFile(consumer, outputPath);
    return drainer.drain();
  }
}