/*
 * #%~
 * VDM Code Generator Runtime
 * %%
 * Copyright (C) 2008 - 2014 Overture
 * %%
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public
 * License along with this program.  If not, see
 * <http://www.gnu.org/licenses/gpl-3.0.html>.
 * #~%
 */
package org.overture.codegen.runtime;

import java.util.Collections;

public class SeqUtil
{
	public static VDMSeq seq()
	{
		return new VDMSeq();
	}
	
	@SuppressWarnings("unchecked")
	public static VDMSeq seq(Object... elements)
	{
		if(elements == null)
			throw new IllegalArgumentException("Cannot instantiate sequence from null");
		
		VDMSeq seq = seq();
		for(Object element : elements)
			seq.add(element);

		return seq;
	}
	
	@SuppressWarnings("unchecked")
	public static VDMSeq mod(VDMSeq seq, Maplet... maplets)
	{
		if(maplets == null)
			throw new IllegalArgumentException("Cannot modify sequence from null");
		
		if(seq == null)
			throw new IllegalArgumentException("Cannot modify null");
		
		for(Maplet maplet : maplets)
		{
			Object left = maplet.getLeft();
			Object right = maplet.getRight();
			
			if(!(left instanceof Long))
				throw new IllegalArgumentException("Domain values of maplets in a sequence modification must be of type nat1");
			
			Long key = (Long) left;
			seq.set(Utils.index(key), right);
		}
		
		return seq;
	}
	
	public static String mod(String string, Maplet... maplets)
	{
		if(maplets == null)
			throw new IllegalArgumentException("Cannot modify sequence from null");

		if(string == null)
			throw new IllegalArgumentException("Cannot modify null");
		
		StringBuilder builder = new StringBuilder(string);
		
		for(Maplet maplet : maplets)
		{
			Object left = maplet.getLeft();
			Object right = maplet.getRight();
			
			if(!(left instanceof Long))
				throw new IllegalArgumentException("Domain values of maplets in a sequence modification must be of type nat1");

			if(!(right instanceof Character))
				throw new IllegalArgumentException("Range values must be characters when modifying a character sequence");
			
			char rightChar = (Character) right;
			
			Long key = (Long) left;
			builder.setCharAt(Utils.index(key), rightChar);
		}
		
		return builder.toString();
	}
	
	@SuppressWarnings("unchecked")
	public static VDMSeq subSeq(VDMSeq seq, Number fromIndex, Number toIndex)
	{
		if(fromIndex.longValue() > toIndex.longValue() || toIndex.longValue() < 1)
		{
			return seq();
		}
		
		fromIndex = Utils.index(Math.max(1, fromIndex.longValue()));
		toIndex = Math.min(seq.size(), toIndex.longValue());
		
		VDMSeq subSeq = seq();
		subSeq.addAll(seq.subList(Utils.toInt(fromIndex), Utils.toInt(toIndex)));
		
		return subSeq;
	}
	
	public static String subSeq(String seq, Number fromIndex, Number toIndex)
	{
		if(fromIndex.longValue() > toIndex.longValue() || toIndex.longValue() < 1)
		{
			return "";
		}
		
		fromIndex = Utils.index(Math.max(1, fromIndex.longValue()));
		toIndex = Math.min(seq.length(), toIndex.longValue());
		
		return seq.substring(Utils.toInt(fromIndex), Utils.toInt(toIndex));
	}
	
	@SuppressWarnings("unchecked")
	public static VDMSet elems(VDMSeq seq)
	{
		if(seq == null)
			throw new IllegalArgumentException("Cannot get elems of null");

		VDMSet elems = SetUtil.set();
		elems.addAll(seq);
		
		return elems;
	}
	
	@SuppressWarnings("unchecked")
	public static VDMSet elems(String string)
	{
		if(string == null)
			throw new IllegalArgumentException("Cannot get elems of null");

		VDMSet elems = SetUtil.set();
		
		for(int i = 0; i < string.length(); i++)
		{
			elems.add(string.charAt(i));
		}
		
		return elems;
	}
	
	@SuppressWarnings("unchecked")
	public static VDMSeq reverse(VDMSeq seq)
	{
		if(seq == null)
			throw new IllegalArgumentException("Cannot reverse null");
		
		VDMSeq result = seq();
		
		result.addAll(seq);
		Collections.reverse(result);
		
		return result;
	}
	
	@SuppressWarnings("unchecked")
	public static VDMSeq tail(VDMSeq seq)
	{
		if(seq == null)
			throw new IllegalArgumentException("Cannot take tail of null");
		
		if(seq.isEmpty())
			throw new IllegalArgumentException("Cannot take tail of empty sequence");
		
		VDMSeq tail = new VDMSeq();
		
		for(int i = 1; i < seq.size(); i++)
		{
			Object element = seq.get(i);
			tail.add(element);
		}
		
		return tail;
	}
	
	public static String tail(String seq)
	{
		if(seq == null)
			throw new IllegalArgumentException("Cannot take tail of null");
		
		if(seq.isEmpty())
			throw new IllegalArgumentException("Cannot take tail of empty string");

		return seq.substring(1, seq.length());
	}
	
	@SuppressWarnings("unchecked")
	public static VDMSet inds(VDMSeq seq)
	{
		if(seq == null)
			throw new IllegalArgumentException("Cannot get indices of null");
		
		VDMSet indices = SetUtil.set();
		
		for(long i = 1; i <= seq.size(); i++)
		{
			indices.add(i);
		}
		
		return indices;
	}
	
	@SuppressWarnings("unchecked")
	public static VDMSet inds(String seq)
	{
		if(seq == null)
			throw new IllegalArgumentException("Cannot get indices of null");
		
		VDMSet indices = SetUtil.set();
		
		for(long i = 1; i <= seq.length(); i++)
		{
			indices.add(i);
		}
		
		return indices;
	}
	
	@SuppressWarnings("unchecked")
	public static VDMSeq conc(VDMSeq left, VDMSeq right)
	{
		if(left == null || right == null)
			throw new IllegalArgumentException("Cannot concatenate null");

		VDMSeq result = seq();

		result.addAll(left);
		result.addAll(right);
		
		return result;
	}
	
	@SuppressWarnings("unchecked")
	public static VDMSeq distConc(VDMSeq sequences)
	{
		if(sequences == null)
			throw new IllegalArgumentException("Distributed concatenation of null is undefined");
		
		VDMSeq result = seq();
		
		for(Object seq : sequences)
		{
			if (seq instanceof String)
			{
				char[] charArray = ((String) seq).toCharArray();
				
				for(Character c : charArray)
				{
					result.add(c);
				}
				
			} else if (seq instanceof VDMSeq)
			{
				VDMSeq vdmSeq = (VDMSeq) seq;
				result.addAll(vdmSeq);
			} else
			{
				throw new IllegalArgumentException("Distributed concatenation only supports sequences");

			}
		}
		
		return result;
	}
	
	public static String distConcStrings(VDMSeq stringSeq)
	{
		if(stringSeq == null)
			throw new IllegalArgumentException("Distributed string concatenation of null is undefined");
	
		String result = "";
		
		for(Object str : stringSeq)
		{
			if(!(str instanceof String))
				throw new IllegalArgumentException("Distributed string concatenation only supports strings");
			
			result += str;
		}
		
		return result;
	}
	
	public static String toStr(Object seq)
	{
		if(seq instanceof VDMSeq)
		{
			VDMSeq vdmSeq = (VDMSeq) seq;
			if (vdmSeq.isEmpty())
			{
				return "";
			} else
			{
				return seq.toString();
			}
		}
		else if(seq instanceof String)
		{
			return (String) seq;
		}
		else
		{
			throw new RuntimeException("String conversion is only supported for VDMSeq");
		}
	}
	
	@SuppressWarnings("unchecked")
	public static VDMSeq toSeq(Object str)
	{
		VDMSeq result = seq();

		if (str instanceof String)
		{
			String string = (String) str;
			for (Character c : string.toCharArray())
			{
				result.add(c);
			}

			return result;
		}
		else if(str instanceof VDMSeq)
		{
			return ((VDMSeq) str).clone();
		}
		else
		{
			throw new RuntimeException("VDMSeq conversion is only supported for strings");
		}
	}
}
