/*
	grive: an GPL program to sync a local directory with Google Drive
	Copyright (C) 2013 Wan Wai Ho

	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 version 2
	of the License.

	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, write to the Free Software
	Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
	MA  02110-1301, USA.
*/

#include "ValBuilder.hh"

#include <cassert>

namespace gr {

ValBuilder::ValBuilder( )
{
}

ValBuilder::~ValBuilder()
{
}

void ValBuilder::Visit( long long t )
{
	Build(Val(t)) ;
}

void ValBuilder::Visit( double t )
{
	Build(Val(t)) ;
}

void ValBuilder::Visit( const std::string& t )
{
	Build(Val(t)) ;
}

void ValBuilder::Visit( bool t )
{
	Build(Val(t)) ;
}

void ValBuilder::VisitNull()
{
	Build(Val()) ;
}

void ValBuilder::Build( const Val& t )
{
	if ( m_ctx.empty() )
	{
		Level l = { Val::Null(), t } ;
		m_ctx.push( l ) ;
	}
	
	else if ( m_ctx.top().val.Is<Val::Array>() )
	{
		Val::Array& ar = m_ctx.top().val.As<Val::Array>() ;
		ar.push_back( t ) ;
	}
	else if ( m_ctx.top().val.Is<Val::Object>() )
	{
		if ( !m_ctx.top().key.Is<std::string>() )
			BOOST_THROW_EXCEPTION( Error() << NoKey_(t) ) ;
	
		else
		{
			Val::Object& obj = m_ctx.top().val.As<Val::Object>() ;
			obj.insert( std::make_pair( m_ctx.top().key.Str(), t ) ) ;
			m_ctx.top().key = Val::Null() ;
		}
	}
	else
		BOOST_THROW_EXCEPTION( Error() << Unexpected_(m_ctx.top().val) ) ;
}

void ValBuilder::VisitKey( const std::string& t )
{
	m_ctx.top().key = t ;
}

void ValBuilder::StartArray()
{
	Level l = { Val::Null(), Val(Val::Array()) } ;
	m_ctx.push(l) ;
}

void ValBuilder::EndArray()
{
	End( Val::array_type ) ;
}

void ValBuilder::End( Val::TypeEnum type )
{
	if ( m_ctx.top().val.Type() == type )
	{
		assert( m_ctx.top().key.Is<void>() ) ;
	
		// get top Val from stack
		Val current ;
		current.Swap( m_ctx.top().val ) ;
		m_ctx.pop() ;
		
		Build(current) ;
	}
}

void ValBuilder::StartObject()
{
	Level l = { Val::Null(), Val( Val::Object() ) } ;
	m_ctx.push(l) ;
}

void ValBuilder::EndObject()
{
	End( Val::object_type ) ;
}

Val ValBuilder::Result() const
{
	assert( m_ctx.size() == 1U ) ;
	return m_ctx.top().val ;
}

} // end of namespace
