/*
 * Copyright (c) 2013, 2020 Oracle and/or its affiliates. All rights reserved.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v. 2.0, which is available at
 * http://www.eclipse.org/legal/epl-2.0.
 *
 * This Source Code may also be made available under the following Secondary
 * Licenses when the conditions for such availability set forth in the
 * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
 * version 2 with the GNU Classpath Exception, which is available at
 * https://www.gnu.org/software/classpath/license.html.
 *
 * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
 */

/*
 * $Id$
 */

package com.sun.ts.tests.jms.ee20.ra.common.messaging;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.System.Logger;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Properties;

import org.junit.jupiter.api.BeforeEach;

import com.sun.ts.tests.jms.commonee.Client;

import jakarta.jms.JMSException;
import jakarta.jms.MessageProducer;
import jakarta.jms.Queue;
import jakarta.jms.QueueConnectionFactory;
import jakarta.jms.Session;

abstract public class QueueClientBase extends Client implements Constants {
	private String currentTestName;

	//////////////////////////////////////////////////////////////////////
	// Queue related stuff
	//////////////////////////////////////////////////////////////////////
	// These are declared in super class jms.commonee.Client
	// protected QueueConnection qConnect;
	// protected QueueSession session;
	// protected QueueConnectionFactory qFactory;
	// protected QueueSender qSender;
	// protected Queue rcvrQueue;
	//
	private Queue sendQ;

	abstract protected void initSendQueue();

	abstract protected void initReceiveQueue();

	abstract protected void initQueueConnectionFactory();

	private static final Logger logger = (Logger) System.getLogger(QueueClientBase.class.getName());

	protected Queue getSendQueue() {
		return sendQ;
	}

	protected void setSendQueue(Queue q) {
		sendQ = q;
	}

	// this receive queue is also used by Topic MDB to send back response.
	protected Queue getReceiveQueue() {
		return rcvrQueue;
	}

	protected void setReceiveQueue(Queue q) {
		rcvrQueue = q;
	}

	protected QueueConnectionFactory getQueueConnectionFactory() {
		return qFactory;
	}

	protected void setQueueConnectionFactory(QueueConnectionFactory qf) {
		qFactory = qf;
	}

	protected String getCurrentTestName() {
		return currentTestName;
	}

	protected void setCurrentTestName(String tn) {
		currentTestName = tn;
	}

	/**
	 * Sends message and waits for response. The message should reach the target
	 * MDB, and a response should be received by this client. Simplified version
	 * since testname can be retrieved from props in setup().
	 */
	protected void sendReceive() throws Exception {
		sendReceive(getCurrentTestName(), 0);
	}

	/**
	 * Sends message and waits for response. The message should not reach the target
	 * MDB, and no response should be received by this client.
	 */
	protected void sendReceiveNegative(String testname, int testnum) throws Exception {
		sendOnly(testname, testnum);
		if (checkOnResponse(testname)) {
			throw new Exception("This is a negative test that expects no response," + " but actually got a response.");
		} else {
			TLogger.log("No response, as expected");
		}
	}

	/**
	 * Sends message and waits for response. The message should reach the target
	 * MDB, and a response should be received by this client.
	 */
	protected void sendReceive(String testname, int testnum) throws Exception {
		sendOnly(testname, testnum);
		// sometimes have to sleep for awhile. But we shouldn't interfere
		// with thread management
		if (!checkOnResponse(testname)) {
			throw new Exception("checkOnResponse for " + testname + " returned false.");
		}
	}

	protected void sendOnly(String testname, int testnum) throws Exception {
		try {
			createTestMessage(testname, testnum);
			MessageProducer producer = getMessageProducer();
			producer.setTimeToLive(MESSAGE_TIME_TO_LIVE);
			producer.send(msg);
			TLogger.log("message sent from testname: " + testname + ", testnum: " + testnum + ", using producer: "
					+ producer);
		} catch (Exception e) {
			throw new Exception(e);
		}
	}

	@BeforeEach
	public void setup() throws Exception {
		setCurrentTestName(System.getProperty(FINDER_TEST_NAME_KEY));
		// initTestProperties(p);

		// used by both queue and topic MDB to send back response
		initReceiveQueue();

		try {
			configureQueue();
			configureTopic();
		} catch (JMSException ex) {
			throw new Exception(ex);
		}
		TLogger.log("get the connection and start up");
		TLogger.log("Client: connection started, now send initialization msg!");
	}

	protected MessageProducer getMessageProducer() throws JMSException {
		qSender = session.createSender(getSendQueue());
		return qSender;
	}

	protected void configureQueue() throws JMSException {
		initQueueConnectionFactory();
		qConnect = qFactory.createQueueConnection(jmsUser, jmsPassword);
		session = qConnect.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
		qConnect.start();
		initSendQueue();
	}

	protected void configureTopic() throws JMSException {
	}

	protected void urlTest(URL url) throws Exception {
		urlTest(url, PASSED);
	}

	protected void urlTest(URL url, String expectedFirstLine) throws Exception {
		HttpURLConnection conn = null;
		InputStream is = null;
		TLogger.log("About to connect to url: ");
		TLogger.log(url.toString());
		try {
			conn = (HttpURLConnection) (url.openConnection());
			int code = conn.getResponseCode();
			if (code != HttpURLConnection.HTTP_OK) {
				throw new Exception("Unexpected return code: " + code);
			}

			is = conn.getInputStream();
			BufferedReader input = new BufferedReader(new InputStreamReader(is));
			String line = input.readLine();
			if (line != null) {
				line = line.trim();
			}
			if (!expectedFirstLine.equals(line)) {
				throw new Exception("Wrong response. Expected: " + expectedFirstLine + ", received: " + line);
			} else {
				TLogger.log("Got expected response: " + line);
			}
			try {
				input.close();
			} catch (IOException e) {
				// ignore
			}
		} catch (IOException e) {
			throw new Exception(e);
		} finally {
			if (is != null) {
				try {
					is.close();
				} catch (IOException e) {
					// ignore
				}
			}
			if (conn != null) {
				conn.disconnect();
			}
		}
	}

	private void initTestProperties(Properties p) throws Exception {
		jmsUser = System.getProperty("user");
		if (jmsUser == null) {
			logger.log(Logger.Level.TRACE, "user is null");
			throw new Exception("Error getting user");
		}

		jmsPassword = System.getProperty("password");
		if (jmsPassword == null) {
			logger.log(Logger.Level.TRACE, "passwd is null");
			throw new Exception("Error getting pwd");
		}

		String time = System.getProperty("jms_timeout");
		if (time == null) {
			logger.log(Logger.Level.TRACE, "time is null");
			throw new Exception("Error getting time");
		}

		timeout = Integer.parseInt(time);
	}
}
