//===-- ThreadPlanTestCondition.cpp ---------------------------------*- C++ -*-===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include "lldb/Target/ThreadPlanTestCondition.h"

// C Includes
// C++ Includes
// Other libraries and framework includes
// Project includes

#include "lldb/lldb-private-log.h"
#include "lldb/Breakpoint/Breakpoint.h"
#include "lldb/Breakpoint/BreakpointLocation.h"
#include "lldb/Core/Log.h"
#include "lldb/Core/Stream.h"
#include "lldb/Core/Value.h"
#include "lldb/Core/ValueObject.h"
#include "lldb/Symbol/Function.h"
#include "lldb/Symbol/Symbol.h"
#include "lldb/Target/Process.h"
#include "lldb/Target/RegisterContext.h"
#include "lldb/Target/StopInfo.h"
#include "lldb/Target/Thread.h"

using namespace lldb;
using namespace lldb_private;


//----------------------------------------------------------------------
// ThreadPlanTestCondition: Step through a stack range, either stepping over or into
// based on the value of \a type.
//----------------------------------------------------------------------

ThreadPlanTestCondition::ThreadPlanTestCondition (Thread& thread,
                                                  ExecutionContext &exe_ctx, 
                                                  ClangUserExpression *expression, 
                                                  const BreakpointLocationSP &break_loc_sp, 
                                                  bool stop_others) :
    ThreadPlan (ThreadPlan::eKindTestCondition, "test condition", thread, eVoteNoOpinion, eVoteNoOpinion),
    m_expression (expression),
    m_exe_ctx (exe_ctx),
    m_break_loc_sp (break_loc_sp),
    m_did_stop (false),
    m_stop_others (stop_others)
{
}

ThreadPlanTestCondition::~ThreadPlanTestCondition ()
{
}

bool
ThreadPlanTestCondition::ValidatePlan (Stream *error)
{
    return true;
}

void 
ThreadPlanTestCondition::GetDescription (Stream *s, lldb::DescriptionLevel level)
{
    if (m_expression)
        s->Printf("Thread plan to test condition: \"%s\".", m_expression->GetUserText());
    else
        s->Printf("Thread plan to test unspecified condition.");
}

bool 
ThreadPlanTestCondition::ShouldStop (Event *event_ptr)
{    
    LogSP log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP));
    if (m_thread.IsThreadPlanDone(m_expression_plan_sp.get()))
    {
        lldb::ClangExpressionVariableSP expr_result;
        StreamString error_stream;
        m_expression->FinalizeJITExecution(error_stream, m_exe_ctx, expr_result);
        
        ValueObjectSP result_sp (expr_result->GetValueObject());
        if (result_sp)
        {
            // FIXME: This is not the right answer, we should have a "GetValueAsBoolean..."
            Scalar scalar_value;
            if (result_sp->ResolveValue (scalar_value))
            {
                if (scalar_value.ULongLong(1) == 0)
                    m_did_stop = false;
                else
                    m_did_stop = true;
            }
            if (log)
                log->Printf("Condition successfully evaluated, result is %s.\n", m_did_stop ? "true" : "false");
        }
        else
        {
            if (log)
                log->Printf("Failed to get a result from the expression, error: \"%s\"\n", error_stream.GetData());
            m_did_stop = true;
        }
    }
    else if (m_exe_ctx.GetThreadRef().WasThreadPlanDiscarded (m_expression_plan_sp.get()))
    {
        if (log)
            log->Printf("ExecuteExpression thread plan was discarded.\n");
        m_did_stop = true; 
    }
    
    // One tricky bit, somebody might have disabled/deleted the breakpoint while we were running this condition, if so we
    // should just continue.  If the breakpoint gets disabled, then its "site" will be null'ed out, so we can't report
    // this as a breakpoint event any more, since we can't reconstruct it's site.  So just pass the event on.
    if (!m_break_loc_sp->IsEnabled())
    {
        m_did_stop = false;
    }
    else
    {
        // Now we have to change the event to a breakpoint event and mark it up appropriately:
        Process::ProcessEventData *new_data = new Process::ProcessEventData (m_thread.GetProcess().GetSP(), eStateStopped);
        event_ptr->SetData(new_data);
        event_ptr->SetType(Process::eBroadcastBitStateChanged);
        SetStopInfo(StopInfo::CreateStopReasonWithBreakpointSiteID (m_thread, 
                                                                    m_break_loc_sp->GetBreakpointSite()->GetID(),
                                                                    m_did_stop));
        if (m_did_stop)
        {
            Process::ProcessEventData::SetRestartedInEvent (event_ptr, false);
        }
        else
        {
            Process::ProcessEventData::SetRestartedInEvent (event_ptr, true);
        }
    }
    SetPlanComplete();
    return m_did_stop;
}

bool
ThreadPlanTestCondition::PlanExplainsStop ()
{
    // We explain all stops, and we just can the execution and return true if we stop for any
    // reason other than that our expression execution is done.
    return true;
}

Vote
ThreadPlanTestCondition::ShouldReportStop (Event *event_ptr)
{
    if (m_did_stop)
    {
        return eVoteYes;
    }
    else 
    {
        return eVoteNo;
    }
}

void
ThreadPlanTestCondition::DidPush()
{
    StreamString error_stream;
    m_expression_plan_sp.reset(m_expression->GetThreadPlanToExecuteJITExpression (error_stream, m_exe_ctx));
    m_thread.QueueThreadPlan (m_expression_plan_sp, false);
}

bool
ThreadPlanTestCondition::StopOthers ()
{
    return m_stop_others;
}

bool
ThreadPlanTestCondition::WillStop ()
{
    return true;
}

StateType
ThreadPlanTestCondition::GetPlanRunState ()
{
    return eStateRunning;
}

bool
ThreadPlanTestCondition::MischiefManaged ()
{
    // If we get a stop we're done, we don't puase in the middle of 
    // condition execution.
    return true;
}
