/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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 org.red5.io.webm;

import java.io.Closeable;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;

import org.red5.io.matroska.ConverterException;
import org.red5.io.matroska.dtd.CompoundTag;
import org.red5.io.matroska.dtd.StringTag;
import org.red5.io.matroska.dtd.Tag;
import org.red5.io.matroska.dtd.TagFactory;
import org.red5.io.matroska.dtd.UnsignedIntegerTag;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Class able to write webm tags to file
 *
 */
public class WebmWriter implements Closeable, TagConsumer {
	private static Logger log = LoggerFactory.getLogger(WebmWriter.class);

	private boolean append;
	private RandomAccessFile dataFile;
	private File file;
	@SuppressWarnings("unused")
	private volatile long bytesWritten;
	private String filePath;

	/**
	 * Constructor
	 * 
	 * @param file - file the data need to written to
	 * @param append - if <code>false</code> the file will be rewritten, if <code>true</code> data will be appended
	 */
	public WebmWriter(File file, boolean append) {
		// the final version of the file will go here
		this.file = file;
		filePath = file.getAbsolutePath();
		try {
			this.append = append;
			if (append) {
				// grab the file we will append to
				if (!file.exists() || !file.canRead() || !file.canWrite()) {
					throw new FileNotFoundException("File does not exist or cannot be accessed");
				} else {
					log.trace("File size: {} last modified: {}", file.length(), file.lastModified());
					// update the bytes written so we write to the correct starting position
					bytesWritten = file.length();
				}
				this.dataFile = new RandomAccessFile(file, "rws");
			} else {
				// temporary data file for storage of stream data
				File dat = new File(filePath + ".ser");
				if (dat.exists()) {
					dat.delete();
					dat.createNewFile();
				}
				this.dataFile = new RandomAccessFile(dat, "rws");
			}
		} catch (Exception e) {
			log.error("Failed to create FLV writer", e);
		}
	}

	/**
	 * method to write webm header to the new file
	 * 
	 * @throws IOException - in case of IO errors
	 * @throws ConverterException - in case of conversion errors
	 */
	public void writeHeader() throws IOException, ConverterException {
		if (append) {
			return;
		}
		try {
			CompoundTag ebml = TagFactory.<CompoundTag>create("EBML")
					.add(TagFactory.<UnsignedIntegerTag>create("EBMLVersion").setValue(1))
					.add(TagFactory.<UnsignedIntegerTag>create("EBMLReadVersion").setValue(1))
					.add(TagFactory.<UnsignedIntegerTag>create("EBMLMaxIDLength").setValue(4))
					.add(TagFactory.<UnsignedIntegerTag>create("EBMLMaxSizeLength").setValue(8))
					.add(TagFactory.<StringTag>create("DocType").setValue("webm"))
					.add(TagFactory.<UnsignedIntegerTag>create("DocTypeVersion").setValue(3))
					.add(TagFactory.<UnsignedIntegerTag>create("DocTypeReadVersion").setValue(2))
					;
			byte[] hb = ebml.encode();
			bytesWritten += hb.length;
			dataFile.write(hb);
		} catch (IOException|ConverterException e) {
			log.error("Failed to write header", e);
			throw e;
		}
	}

	/**
	 * will write tag in "dom" mode, equivalent to {@link WebmWriter#writeTag(Tag, false)}
	 * 
	 * @param tag - webm tag to be written
	 * @throws IOException - in case of any IO errors
	 */
	public void writeTag(Tag tag) throws IOException {
		writeTag(tag, false);
	}

	/**
	 * will write tag in given mode: in "sax" mode "master" tags will not encode their children
	 * bytesWritten counter will be increased by the number of bytes actually written
	 * 
	 * @param tag - tag to be written
	 * @param saxMode - "sax" mode if <code>true</code>, "dom" mode otherwise
	 * @throws IOException - in case of any IO errors
	 */
	public void writeTag(Tag tag, boolean saxMode) throws IOException {
		byte[] hb = tag.encode(saxMode);
		bytesWritten += hb.length;
		dataFile.write(hb);
	}

	/**
	 * Will close all opened resources and "finalize" the write process
	 */
	@Override
	public void close() throws IOException {
		if (dataFile != null) {
			//TODO create separate method for this
			if (!append) {
				dataFile.seek(0);
				try (RandomAccessFile rf = new RandomAccessFile(file, "rw")) {
					rf.getChannel().transferFrom(dataFile.getChannel(), 0, dataFile.length());
				}
			}
			try {
				dataFile.close();
				dataFile = null;
			} catch (Throwable th) {
				//no-op
			}
		}
	}

	/**
	 * @see org.red5.io.webm.TagConsumer#consume(org.red5.io.matroska.dtd.Tag)
	 */
	@Override
	public void consume(Tag tag) throws IOException {
		//TODO add mode switch
		writeTag(tag, true);
	}
}

@@ -3964,356 +3964,8 @@
  tag
- in %22dom%22 mode, equivalent to %7B@link WebmWriter#writeTag(Tag, false)%7D%0A%09 * %0A%09 * @param tag - webm tag to be written%0A%09 * @throws IOException - in case of any IO errors%0A%09 */%0A%09public void writeTag(Tag tag) throws IOException %7B%0A%09%09writeTag(tag, false);%0A%09%7D%0A%0A%09/**%0A%09 * will write tag in given mode: in %22sax%22 mode %22master%22 tags will not encode their children
 %0A%09 *
@@ -4088,83 +4088,8 @@
 ten%0A
-%09 * @param saxMode - %22sax%22 mode if %3Ccode%3Etrue%3C/code%3E, %22dom%22 mode otherwise%0A
 %09 * 
@@ -4173,25 +4173,8 @@
  tag
-, boolean saxMode
 ) th
@@ -4221,15 +4221,8 @@
 ode(
-saxMode
 );%0A%09
@@ -4969,14 +4969,8 @@
 (tag
-, true
 );%0A%09

update respectively with 74f38840f07dd20122818f0a99a32d376799a506