/** {@inheritDoc} */
  @Override
  public void write(GatheringByteChannel outputchannel) {

    try {
      int numimages = numImages();

      // Encode each image
      ByteBuffer[] images = new ByteBuffer[numimages];
      for (int i = 0; i < images.length; i++) {
        ByteBuffer image =
            com.mikeduvall.redhorizon.util.ByteBufferFactory.createLittleEndianByteBuffer(
                shpimages[i].capacity());
        CodecUtility.encodeFormat80(shpimages[i], image);
        images[i] = image;
      }

      // Construct image offset headers for each image
      ByteBuffer[] offsets = new ByteBuffer[numimages + 2];
      int offsettotal =
          ShpFileHeaderCNC.HEADER_SIZE + (ShpImageOffsetCNC.OFFSET_SIZE * offsets.length);
      for (int i = 0; i < numImages(); i++) {
        offsets[i] = new ShpImageOffsetCNC(offsettotal, FORMAT80, 0, (byte) 0).toByteBuffer();
        offsettotal += images[i].limit();
      }

      // The 2 special image offsets at the end of the offset array
      offsets[numimages] = new ShpImageOffsetCNC(offsettotal, (byte) 0, 0, (byte) 0).toByteBuffer();
      offsets[numimages + 1] = new ShpImageOffsetCNC(0, (byte) 0, 0, (byte) 0).toByteBuffer();

      // Build header
      ByteBuffer header = shpfileheader.toByteBuffer();

      // Write file
      try {
        outputchannel.write(header);
        outputchannel.write(offsets);
        outputchannel.write(images);
      } catch (IOException e) {
        throw new RuntimeException(e);
      }
    } finally {
      try {
        outputchannel.close();
      } catch (IOException e) {
        throw new RuntimeException(e);
      }
    }
  }
  /** {@inheritDoc} */
  @Override
  public void write(GatheringByteChannel outputchannel) {

    int numimages = numImages();

    // Build header
    ByteBuffer header = wsaheader.toByteBuffer();

    // Build palette
    ByteBuffer palette = wsapalette.toByteBuffer();

    // Encode each frame, construct matching offsets
    ByteBuffer[] frames = new ByteBuffer[isLooping() ? numimages + 1 : numimages];
    ByteBuffer lastbytes =
        com.mikeduvall.redhorizon.util.ByteBufferFactory.createLittleEndianByteBuffer(
            width() * height());

    ByteBuffer frameoffsets =
        com.mikeduvall.redhorizon.util.ByteBufferFactory.createLittleEndianByteBuffer(
            (numimages + 2) * 4);
    int offsettotal = WsaFileHeaderCNC.HEADER_SIZE + ((numimages + 2) * 4);

    for (int i = 0; i < frames.length; i++) {
      ByteBuffer framebytes = wsaframes[i];
      ByteBuffer frameint =
          com.mikeduvall.redhorizon.util.ByteBufferFactory.createLittleEndianByteBuffer(
              (int) (framebytes.capacity() * 1.5));
      ByteBuffer frame =
          com.mikeduvall.redhorizon.util.ByteBufferFactory.createLittleEndianByteBuffer(
              (int) (framebytes.capacity() * 1.5));

      // First encode in Format40, then Format80
      CodecUtility.encodeFormat40(framebytes, frameint, lastbytes);
      CodecUtility.encodeFormat80(frameint, frame);

      frames[i] = frame;
      lastbytes = framebytes;

      frameoffsets.putInt(offsettotal);
      offsettotal += frame.limit();
    }

    // Last offset for EOF
    frameoffsets.putInt(offsettotal);
    frameoffsets.rewind();

    // Write file to disk
    try {
      outputchannel.write(header);
      outputchannel.write(frameoffsets);
      outputchannel.write(palette);
      outputchannel.write(frames);
    } catch (IOException e) {
      throw new RuntimeException(e);
    }

    // Generate high-res colour lookup table
    if (!srcnohires) {

      // Figure-out the appropriate file name
      String lookupname =
          filename.contains(".")
              ? filename.substring(0, filename.lastIndexOf('.')) + ".pal"
              : filename + ".pal";

      // Write the index of the closest interpolated palette colour
      // TODO: Perform proper colour interpolation
      ByteBuffer lookup =
          com.mikeduvall.redhorizon.util.ByteBufferFactory.createLittleEndianByteBuffer(256);
      for (int i = 0; i < 256; i++) {
        lookup.put((byte) i);
      }
      lookup.rewind();

      try (FileChannel lookupfile = FileChannel.open(Paths.get(lookupname), WRITE)) {
        for (int i = 0; i < 256; i++) {
          lookupfile.write(lookup);
        }
      }
      // TODO: Should be able to soften the auto-close without needing this
      catch (IOException ex) {
        throw new RuntimeException(ex);
      }
    }
  }