using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
using System.Xml.Schema;
using Patcher.Data.Command;
using Patcher.DB;

namespace Patcher.Data.Patch
{
	class AtomicPatch : AbstractPatch
	{

		private readonly AbstractCommand[] commands;
		private readonly bool isStrictCommandSet;
	
		public AtomicPatch(AbstractCommand[] commands, bool isStrictCommandSet, HashSet<string> restrictToEnvironments, Context context) : base(restrictToEnvironments, context)
		{
			this.commands = commands;
			this.isStrictCommandSet = isStrictCommandSet;
		}
		
		private void CheckDbDriver()
		{
			if(!this.isStrictCommandSet)
			{
				if(!context.DbDriver.IsDDLTransactional)
				{
					throw new ApplicationException("You should use strictCommandSet when working with non-DDL-transactional DB");
				}
			}
		}

		public override XDocument Apply(Transaction transaction)
		{
			this.CheckDbDriver();
		
			List<XElement> rollbackInfos = new List<XElement>();
			for(int i=0; i<this.commands.Length; i++)
			{
				IEnumerable<XElement> commandRollbackInfoContent;
				if (commands[i] is AbstractPersistentCommand)
				{
					commandRollbackInfoContent = ((AbstractPersistentCommand)commands[i]).Apply(transaction, true);
				} else
				{
					commandRollbackInfoContent = commands[i].Apply(transaction);
				}
				rollbackInfos.Add(new XElement("command", new XAttribute("num", i), commandRollbackInfoContent));
			}
			return new XDocument(new XElement("rollbackInfo", rollbackInfos.ToArray()));
		}
		
		public override void Rollback(Transaction transaction, XDocument rollbackInfo)
		{
			this.CheckDbDriver();
		
			for(int i=this.commands.Length-1; i>=0; i--)
			{
				commands[i].Rollback(transaction, (from commandRollbackInfo in rollbackInfo.Root.Elements("command") where commandRollbackInfo.Attribute("num").Value == i.ToString() select commandRollbackInfo).Single());
			}
		}
	
	}
}
