/*
 * 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.apache.sysds.runtime.instructions;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.sysds.common.InstructionType;
import org.apache.sysds.common.Opcodes;
import org.apache.sysds.runtime.DMLRuntimeException;
import org.apache.sysds.runtime.instructions.ooc.AggregateTernaryOOCInstruction;
import org.apache.sysds.runtime.instructions.ooc.AggregateUnaryOOCInstruction;
import org.apache.sysds.runtime.instructions.ooc.BinaryOOCInstruction;
import org.apache.sysds.runtime.instructions.ooc.CSVReblockOOCInstruction;
import org.apache.sysds.runtime.instructions.ooc.CentralMomentOOCInstruction;
import org.apache.sysds.runtime.instructions.ooc.CtableOOCInstruction;
import org.apache.sysds.runtime.instructions.ooc.IndexingOOCInstruction;
import org.apache.sysds.runtime.instructions.ooc.DataGenOOCInstruction;
import org.apache.sysds.runtime.instructions.ooc.OOCInstruction;
import org.apache.sysds.runtime.instructions.ooc.ParameterizedBuiltinOOCInstruction;
import org.apache.sysds.runtime.instructions.ooc.ReblockOOCInstruction;
import org.apache.sysds.runtime.instructions.ooc.TernaryOOCInstruction;
import org.apache.sysds.runtime.instructions.ooc.TSMMOOCInstruction;
import org.apache.sysds.runtime.instructions.ooc.UnaryOOCInstruction;
import org.apache.sysds.runtime.instructions.ooc.MMultOOCInstruction;
import org.apache.sysds.runtime.instructions.ooc.MapMMChainOOCInstruction;
import org.apache.sysds.runtime.instructions.ooc.ReorgOOCInstruction;
import org.apache.sysds.runtime.instructions.ooc.TeeOOCInstruction;
import org.apache.sysds.runtime.instructions.ooc.AppendOOCInstruction;
import org.apache.sysds.runtime.instructions.ooc.ReshapeOOCInstruction;
import org.apache.sysds.runtime.instructions.ooc.QuaternaryOOCInstruction;

public class OOCInstructionParser extends InstructionParser {
	protected static final Log LOG = LogFactory.getLog(OOCInstructionParser.class.getName());

	public static OOCInstruction parseSingleInstruction(String str) {
		if(str == null || str.isEmpty())
			return null;
		InstructionType ooctype = InstructionUtils.getOOCType(str);
		if(ooctype == null)
			throw new DMLRuntimeException("Unable derive ooctype for instruction: " + str);
		OOCInstruction oocinst = parseSingleInstruction(ooctype, str);
		if(oocinst == null)
			throw new DMLRuntimeException("Unable to parse instruction: " + str);
		return oocinst;
	}

	public static OOCInstruction parseSingleInstruction(InstructionType ooctype, String str) {
		if(str == null || str.isEmpty())
			return null;
		switch(ooctype) {
			case Reblock:
				return ReblockOOCInstruction.parseInstruction(str);
			case CSVReblock:
				return CSVReblockOOCInstruction.parseInstruction(str);
			case AggregateUnary:
				return AggregateUnaryOOCInstruction.parseInstruction(str);
			case AggregateTernary:
				return AggregateTernaryOOCInstruction.parseInstruction(str);
			case Unary:
				return UnaryOOCInstruction.parseInstruction(str);
			case Binary:
				return BinaryOOCInstruction.parseInstruction(str);
			case Builtin:
				String[] parts = InstructionUtils.getInstructionPartsWithValueType(str);
				if(parts[0].equals(Opcodes.LOG.toString()) || parts[0].equals(Opcodes.LOGNZ.toString())) {
					if(parts.length == 3)
						return UnaryOOCInstruction.parseInstruction(str);
					else if(parts.length == 4)
						return BinaryOOCInstruction.parseInstruction(str);
				}
				throw new DMLRuntimeException("Invalid Builtin Instruction: " + str);
			case Ternary:
				return TernaryOOCInstruction.parseInstruction(str);
			case AggregateBinary:
			case MAPMM:
				return MMultOOCInstruction.parseInstruction(str);
			case MAPMMCHAIN:
				return MapMMChainOOCInstruction.parseInstruction(str);
			case MMTSJ:
				return TSMMOOCInstruction.parseInstruction(str);
			case Reorg:
				return ReorgOOCInstruction.parseInstruction(str);
			case Reshape:
				return ReshapeOOCInstruction.parseInstruction(str);
			case Tee:
				return TeeOOCInstruction.parseInstruction(str);
			case CentralMoment:
				return  CentralMomentOOCInstruction.parseInstruction(str);
			case Ctable:
				return CtableOOCInstruction.parseInstruction(str);
			case ParameterizedBuiltin:
				return ParameterizedBuiltinOOCInstruction.parseInstruction(str);
			case MatrixIndexing:
				return IndexingOOCInstruction.parseInstruction(str);
			case Rand:
				return DataGenOOCInstruction.parseInstruction(str);
			case Append:
				return AppendOOCInstruction.parseInstruction(str);
			case Quaternary:
				return QuaternaryOOCInstruction.parseInstruction(str);

			default:
				throw new DMLRuntimeException("Invalid OOC Instruction Type: " + ooctype);
		}
	}
}
